merge upstream 1.50.3

This commit is contained in:
Yue-Lan 2023-04-07 11:45:59 +08:00
parent e118d16b14
commit cfb097cdca
126 changed files with 41792 additions and 31832 deletions

43
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,43 @@
fedora:
image:
name: registry.gitlab.gnome.org/gnome/gvfs:latest
entrypoint: ["/bin/sh", "-c"]
tags:
- x86_64
- ipv6
script:
- meson
-Dinstalled_tests=true
-Ddevel_utils=true
-Dman=true
-Dafc=true
-Darchive=true
-Dsftp=true
-Dsmb=true
-Dudisks2=true
-Dhttp=true
-Dgphoto2=true
--prefix /usr --werror build
- sudo ninja -C build install
- GIO_USE_VOLUME_MONITOR=unix gnome-desktop-testing-runner gvfs
allow_failure: true
update-image:
variables:
STORAGE_DRIVER: vfs
BUILDAH_FORMAT: docker
BUILDAH_ISOLATION: chroot
image: registry.fedoraproject.org/fedora:latest
script:
- dnf install -y buildah runc
- sed -i '/^mountopt =.*/d' /etc/containers/storage.conf
- buildah bud --tag $CI_REGISTRY_IMAGE -f .gitlab-ci/Dockerfile
- buildah tag $CI_REGISTRY_IMAGE "$CI_REGISTRY_IMAGE:v$CI_JOB_ID"
- buildah login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- buildah push --creds $CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD $CI_REGISTRY_IMAGE
- buildah push --creds $CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD "$CI_REGISTRY_IMAGE:v$CI_JOB_ID"
when: manual
only:
variables:
- $CI_PROJECT_NAMESPACE == "GNOME"

46
.gitlab-ci/Dockerfile Normal file
View File

@ -0,0 +1,46 @@
FROM fedora:latest
RUN dnf install --nogpg -y dnf-plugins-core git gnome-desktop-testing dbus-daemon python3-twisted python3-gobject procps-ng bzip2 httpd mod_ssl openssh-server passwd gcc-c++ \
&& dnf builddep --nogpg -y gvfs \
&& dnf clean all
RUN dnf builddep --nogpg -y glib \
&& dnf clean all \
&& git clone --depth 1 https://gitlab.gnome.org/GNOME/glib.git \
&& cd glib \
&& meson . _build --prefix=/usr \
&& ninja -C _build \
&& ninja -C _build install \
&& cd .. \
&& rm -rf glib
RUN dnf builddep --nogpg -y glib-networking \
&& dnf clean all \
&& git clone --depth 1 https://gitlab.gnome.org/GNOME/glib-networking.git \
&& cd glib-networking \
&& meson . _build --prefix=/usr \
&& ninja -C _build \
&& ninja -C _build install \
&& cd .. \
&& rm -rf glib-networking
RUN dnf builddep --nogpg -y libsoup \
&& dnf install -y --nogpg libnghttp2-devel \
&& dnf clean all \
&& git clone --depth 1 https://gitlab.gnome.org/GNOME/libsoup.git \
&& cd libsoup \
&& meson . _build --prefix=/usr \
&& ninja -C _build \
&& ninja -C _build install \
&& cd .. \
&& rm -rf libsoup
RUN sed -i -e 's/# %wheel/%wheel/' /etc/sudoers
RUN useradd -G wheel -m user
RUN passwd -d user
USER user
WORKDIR /home/user
ENV USER user
ENV XDG_RUNTIME_DIR /home/user
RUN ssh-keygen -t rsa -q -N "" -f ~/.ssh/id_rsa

View File

@ -8,6 +8,6 @@ GVfs source repository is at https://gitlab.gnome.org/GNOME/gvfs.
## Code Contribution ## Code Contribution
See https://wiki.gnome.org/GitLab for general informations about GitLab workflow See https://wiki.gnome.org/GitLab for general information about GitLab workflow
for code contribution. GVfs still uses linear GIT history without merge commits, for code contribution. GVfs still uses linear GIT history without merge commits,
please see general commit guidelines at https://wiki.gnome.org/Git/CommitMessages. please see general commit guidelines at https://wiki.gnome.org/Git/CommitMessages.

118
NEWS
View File

@ -1,3 +1,121 @@
Major changes in 1.50.3
=======================
* dav: Prevent usage of NULL when user is not specified (Ondrej Holy)
* ftp: Fix hangs when the connection is released (wangrong)
* fuse: Decrease file handle reference when open file fail (wangrong)
* sftp: PATH-expand the ssh client (Alex Stewart)
* test: Several smaller enhancements (Sébastien Bacher)
* backend: Add support for xx-large and x-large thumbnails (Ondrej Holy)
* goa: Prevent automounts when resuming from suspension (Ondrej Holy)
* Translation updates (GNOME Translation Project contributors)
Major changes in 1.50.2
=======================
* smb: Rework anonymous handling to avoid EINVAL (Ondrej Holy)
* http: Unescape prefix to fix handling of encoded URIs (Ondrej Holy)
* build: Fix build without Avahi support (Ondrej Holy)
Major changes in 1.50.1
=======================
* dav: Drop user from URI as a workaround for Nextcloud bug (Ondrej Holy)
* dav: Port DNS-SD resolver to async API to fix hangs when mounting (Ondrej Holy)
* smb: Ignore EINVAL for kerberos/ccache login (Ondrej Holy)
* dav: Rewrite to libsoup async API to fix crashes (Daniel Kolesa)
* dav: Do not lose userinfo when copying URIs (Daniel Kolesa)
Major changes in 1.50.0
=======================
* Translation updates
Major changes in 1.49.90
========================
* http/dav: Port to libsoup3 (Daniel Kolesa)
* http: Do not silently accept invalid certificates (Daniel Kolesa)
* build: Remove incorrect i18n.merge_file argument to fix build (Ondrej Holy)
* Translation updates
Major changes in 1.49.1
=======================
* sftp: Adapt on new OpenSSH password prompts
* build: Set of improvements for meson
* Add PartOf=graphical-session.target to all systemd units
* Move systemd services to session slice
* Translation updates
Major changes in 1.48.1
=======================
* build: Use install prefix in systemd files paths
* client: Prevent socket leaks if socket dir is inaccessible from client
* admin: Fix regressions caused by port to named sockets
* udisks2: Report unmount progress after showing blocking processes
* Translation updates
Major changes in 1.48.0
=======================
* Translation updates
Major changes in 1.47.91
========================
* Translation updates
Major changes in 1.47.90
========================
* google: Add Shared drives folder
* google: Add Shared with me folder
* google: Improve performance for folders with large number of files
* daemon: Set filesystem::use-preview explicitly and consistently
* daemon: Unify and shorten strings for prompt dialog titles
* Translation updates
Major changes in 1.47.1
=======================
* mtp: Fix crashes when storage descriptions are not provided
* trash: Explicitly cancel file monitor to prevent deadlock
* admin: Add copy and push implementations for better performance
* client: Add fallback to session bus for synchronous API
* daemon: Use named sockets to avoid network permission requirement
* smb: Set fast content type independently of other attributes
* ftp: Prevent source file removal in case of transfer failure
* google: Report progress for file transfers from local filesystem
* sftp: Add support for two factor authentication
* sftp: Use connection multiplexing instead of multiple connections
* smb: Report progress when move operation is done
* google: Set the display name for the root folder also
* Translation updates
Major changes in 1.46.1
=======================
* trash: Do not fail when G_FILE_COPY_NO_FALLBACK_FOR_MOVE is used
* dav: Be sure that enumeration is possible when looking for a root
Major changes in 1.46.0
=======================
* No changes
Major changes in 1.45.92
========================
* build: Link libgvfscommon to libmetadata
* Translation updates
Major changes in 1.45.90
========================
* trash: Add support for x-gvfs-notrash option to ignore mounts
* recent: Port to GDateTime
* Translation updates
Major changes in 1.45.3
=======================
* client: Add support for zone identifiers in IPv6 addresses
* afc: Add support for libplist-2.2
* Translation updates
Major changes in 1.45.2
=======================
* metadata: Emit D-Bus signal when metadata are modified
* sftp: Preserve timestamps during copy and move operations
* dav: Add support for Negotiate and NTLM authentication
* Translation updates
Major changes in 1.44.1 Major changes in 1.44.1
======================= =======================
* udisks2: Fix several memory leaks * udisks2: Fix several memory leaks

View File

@ -7,15 +7,16 @@ implement volume monitors and persistent metadata storage. There is also FUSE
support that provides limited access to the GVfs filesystems for applications support that provides limited access to the GVfs filesystems for applications
not using GIO. not using GIO.
For more info about GVfs see https://wiki.gnome.org/Projects/gvfs. For more info about GVfs, see https://wiki.gnome.org/Projects/gvfs.
## Reporting Bugs ## Reporting Bugs
Bug reports can be found and filed at https://gitlab.gnome.org/GNOME/gvfs/issues. Bug reports can be found and filed at https://gitlab.gnome.org/GNOME/gvfs/issues.
For security related issues, please use [security@gnome.org](mailto:security@gnome.org). For security-related issues, please use https://security.gnome.org/.
See https://wiki.gnome.org/Projects/gvfs/debugging for info about getting debug
logs.
## Ask Questions ## Ask Questions
For questions use gvfs mailing list [gvfs-list@gnome.org](mailto:gvfs-list@gnome.org). For questions use [GNOME Discourse](https://discourse.gnome.org/).
See https://mail.gnome.org/mailman/listinfo/gvfs-list for subscription info.
Alternatively you use irc://irc.gnome.org/nautilus.

View File

@ -438,6 +438,17 @@ create_proxy_for_file2 (GFile *file1,
} }
connection = _g_dbus_connection_get_sync (mount_info1->dbus_id, cancellable, &local_error); connection = _g_dbus_connection_get_sync (mount_info1->dbus_id, cancellable, &local_error);
if (connection == NULL && !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_dbus_error_strip_remote_error (local_error);
g_warning ("The peer-to-peer connection failed: %s. Falling back to the "
"session bus. Your application is probably missing "
"--filesystem=xdg-run/gvfsd privileges.", local_error->message);
g_clear_error (&local_error);
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, cancellable, &local_error);
}
if (connection == NULL) if (connection == NULL)
goto out; goto out;
@ -613,8 +624,19 @@ async_got_connection_cb (GDBusConnection *connection,
if (connection == NULL) if (connection == NULL)
{ {
/* TODO: we should probably test if we really want a session bus; g_dbus_error_strip_remote_error (io_error);
* for now, this code is on par with the old dbus code */
if (g_error_matches (io_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_task_return_error (data->task, g_error_copy (io_error));
async_proxy_create_free (data);
return;
}
g_warning ("The peer-to-peer connection failed: %s. Falling back to the "
"session bus. Your application is probably missing "
"--filesystem=xdg-run/gvfsd privileges.", io_error->message);
g_bus_get (G_BUS_TYPE_SESSION, g_bus_get (G_BUS_TYPE_SESSION,
g_task_get_cancellable (data->task), g_task_get_cancellable (data->task),
bus_get_cb, bus_get_cb,
@ -2689,14 +2711,6 @@ file_transfer (GFile *source,
return FALSE; return FALSE;
} }
if (!native_transfer && remove_source &&
(flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE))
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
return FALSE;
}
if (!native_transfer && local_path == NULL) if (!native_transfer && local_path == NULL)
{ {
/* This will cause the fallback code to be involved */ /* This will cause the fallback code to be involved */

View File

@ -178,6 +178,17 @@ g_daemon_file_monitor_new (const char *remote_id,
daemon_monitor->remote_obj_path = g_strdup (remote_obj_path); daemon_monitor->remote_obj_path = g_strdup (remote_obj_path);
connection = _g_dbus_connection_get_sync (daemon_monitor->remote_id, NULL, &error); connection = _g_dbus_connection_get_sync (daemon_monitor->remote_id, NULL, &error);
if (connection == NULL && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_dbus_error_strip_remote_error (error);
g_warning ("The peer-to-peer connection failed: %s. Falling back to the "
"session bus. Your application is probably missing "
"--filesystem=xdg-run/gvfsd privileges.", error->message);
g_clear_error (&error);
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
}
if (connection == NULL) if (connection == NULL)
{ {
g_printerr ("Error getting connection for monitoring: %s (%s, %d)\n", g_printerr ("Error getting connection for monitoring: %s (%s, %d)\n",

View File

@ -315,8 +315,19 @@ async_got_connection_cb (GDBusConnection *connection,
if (connection == NULL) if (connection == NULL)
{ {
/* TODO: we should probably test if we really want a session bus; g_dbus_error_strip_remote_error (io_error);
* for now, this code is on par with the old dbus code */
if (g_error_matches (io_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_task_return_error (task, g_error_copy (io_error));
g_object_unref (task);
return;
}
g_warning ("The peer-to-peer connection failed: %s. Falling back to the "
"session bus. Your application is probably missing "
"--filesystem=xdg-run/gvfsd privileges.", io_error->message);
g_bus_get (G_BUS_TYPE_SESSION, g_bus_get (G_BUS_TYPE_SESSION,
g_task_get_cancellable (task), g_task_get_cancellable (task),
bus_get_cb, bus_get_cb,

View File

@ -1434,7 +1434,7 @@ g_daemon_vfs_deserialize_icon (GVfs *vfs,
GDBusConnection * GDBusConnection *
_g_daemon_vfs_get_async_bus (void) _g_daemon_vfs_get_async_bus (void)
{ {
return the_vfs ? the_vfs->async_bus : NULL; return the_vfs->async_bus;
} }
static gboolean static gboolean

View File

@ -31,12 +31,14 @@
#include <errno.h> #include <errno.h>
#include <glib/gi18n-lib.h> #include <glib/gi18n-lib.h>
#include <glib/gstdio.h>
#include <gio/gio.h> #include <gio/gio.h>
#include "gvfsdaemondbus.h" #include "gvfsdaemondbus.h"
#include <gvfsdaemonprotocol.h> #include <gvfsdaemonprotocol.h>
#include <gdaemonvfs.h> #include <gdaemonvfs.h>
#include <gvfsdbus.h> #include <gvfsdbus.h>
#include <gvfsutils.h>
/* Extra vfs-specific data for GDBusConnections */ /* Extra vfs-specific data for GDBusConnections */
typedef struct { typedef struct {
@ -156,6 +158,7 @@ set_connection_for_async (GDBusConnection *connection, const char *dbus_id)
typedef struct { typedef struct {
char *dbus_id; char *dbus_id;
GVfsDBusDaemon *proxy;
GDBusConnection *connection; GDBusConnection *connection;
GCancellable *cancellable; GCancellable *cancellable;
@ -174,6 +177,7 @@ async_call_finish (AsyncDBusCall *async_call)
async_call->io_error, async_call->io_error,
async_call->callback_data); async_call->callback_data);
g_clear_object (&async_call->proxy);
g_clear_object (&async_call->connection); g_clear_object (&async_call->connection);
g_clear_object (&async_call->cancellable); g_clear_object (&async_call->cancellable);
g_clear_error (&async_call->io_error); g_clear_error (&async_call->io_error);
@ -260,32 +264,67 @@ async_get_connection_response (GVfsDBusDaemon *proxy,
g_free (address1); g_free (address1);
} }
static void
socket_dir_query_info_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
AsyncDBusCall *async_call = user_data;
g_autoptr (GFileInfo) socket_dir_info = NULL;
socket_dir_info = g_file_query_info_finish (G_FILE (source_object),
res,
&async_call->io_error);
if (socket_dir_info == NULL ||
!g_file_info_get_attribute_boolean (socket_dir_info,
G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE))
{
if (!async_call->io_error)
async_call->io_error = g_error_new_literal (G_IO_ERROR,
G_IO_ERROR_PERMISSION_DENIED,
_("Permission denied"));
async_call_finish (async_call);
return;
}
g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (async_call->proxy), G_VFS_DBUS_TIMEOUT_MSECS);
gvfs_dbus_daemon_call_get_connection (async_call->proxy,
async_call->cancellable,
(GAsyncReadyCallback) async_get_connection_response,
async_call);
}
static void static void
open_connection_async_cb (GObject *source_object, open_connection_async_cb (GObject *source_object,
GAsyncResult *res, GAsyncResult *res,
gpointer user_data) gpointer user_data)
{ {
GVfsDBusDaemon *proxy;
AsyncDBusCall *async_call = user_data; AsyncDBusCall *async_call = user_data;
GError *error = NULL; GError *error = NULL;
g_autofree gchar *socket_dir_path = NULL;
proxy = gvfs_dbus_daemon_proxy_new_finish (res, &error); g_autoptr (GFile) socket_dir = NULL;
if (proxy == NULL)
async_call->proxy = gvfs_dbus_daemon_proxy_new_finish (res, &error);
if (async_call->proxy == NULL)
{ {
async_call->io_error = g_error_copy (error); async_call->io_error = g_error_copy (error);
g_error_free (error); g_error_free (error);
async_call_finish (async_call); async_call_finish (async_call);
return; return;
} }
g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (proxy), G_VFS_DBUS_TIMEOUT_MSECS); /* This is needed to prevent socket leaks. */
socket_dir_path = gvfs_get_socket_dir ();
gvfs_dbus_daemon_call_get_connection (proxy, socket_dir = g_file_new_for_path (socket_dir_path);
async_call->cancellable, g_file_query_info_async (socket_dir,
(GAsyncReadyCallback) async_get_connection_response, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
async_call); G_FILE_QUERY_INFO_NONE,
G_PRIORITY_DEFAULT,
g_object_unref (proxy); async_call->cancellable,
socket_dir_query_info_cb,
user_data);
} }
static void static void
@ -522,6 +561,9 @@ _g_dbus_connection_get_sync (const char *dbus_id,
gchar *address1; gchar *address1;
GVfsDBusDaemon *daemon_proxy; GVfsDBusDaemon *daemon_proxy;
gboolean res; gboolean res;
g_autofree gchar *socket_dir_path = NULL;
g_autoptr (GFile) socket_dir = NULL;
g_autoptr (GFileInfo) socket_dir_info = NULL;
if (g_cancellable_set_error_if_cancelled (cancellable, error)) if (g_cancellable_set_error_if_cancelled (cancellable, error))
return NULL; return NULL;
@ -591,6 +633,26 @@ _g_dbus_connection_get_sync (const char *dbus_id,
if (daemon_proxy == NULL) if (daemon_proxy == NULL)
return NULL; return NULL;
/* This is needed to prevent socket leaks. */
socket_dir_path = gvfs_get_socket_dir ();
socket_dir = g_file_new_for_path (socket_dir_path);
socket_dir_info = g_file_query_info (socket_dir,
G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
G_FILE_QUERY_INFO_NONE,
cancellable,
error);
if (socket_dir_info == NULL ||
!g_file_info_get_attribute_boolean (socket_dir_info,
G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE))
{
if (error && !*error)
*error = g_error_new_literal (G_IO_ERROR,
G_IO_ERROR_PERMISSION_DENIED,
_("Permission denied"));
return NULL;
}
address1 = NULL; address1 = NULL;
res = gvfs_dbus_daemon_call_get_connection_sync (daemon_proxy, res = gvfs_dbus_daemon_call_get_connection_sync (daemon_proxy,
&address1, &address1,

View File

@ -43,7 +43,8 @@
#include <gvfsdbus.h> #include <gvfsdbus.h>
#include <gvfsutils.h> #include <gvfsutils.h>
#define FUSE_USE_VERSION 26 #define FUSE_USE_VERSION 30
#include <fuse.h> #include <fuse.h>
#include <fuse_lowlevel.h> #include <fuse_lowlevel.h>
@ -859,7 +860,7 @@ getattr_for_file_handle (FileHandle *fh, struct stat *sbuf)
} }
static gint static gint
vfs_getattr (const gchar *path, struct stat *sbuf) vfs_getattr (const gchar *path, struct stat *sbuf, struct fuse_file_info *fi)
{ {
GFile *file; GFile *file;
gint result = 0; gint result = 0;
@ -1090,6 +1091,9 @@ open_common (const gchar *path, struct fuse_file_info *fi, GFile *file, int outp
g_mutex_unlock (&fh->mutex); g_mutex_unlock (&fh->mutex);
if (result < 0)
file_handle_unref (fh);
/* The added reference to the file handle is released in vfs_release() */ /* The added reference to the file handle is released in vfs_release() */
return result; return result;
} }
@ -1581,12 +1585,12 @@ readdir_for_file (GFile *base_file, gpointer buf, fuse_fill_dir_t filler)
return result; return result;
} }
filler (buf, ".", NULL, 0); filler (buf, ".", NULL, 0, 0);
filler (buf, "..", NULL, 0); filler (buf, "..", NULL, 0, 0);
while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &error)) != NULL) while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &error)) != NULL)
{ {
filler (buf, g_file_info_get_name (file_info), NULL, 0); filler (buf, g_file_info_get_name (file_info), NULL, 0, 0);
g_object_unref (file_info); g_object_unref (file_info);
} }
@ -1597,7 +1601,7 @@ readdir_for_file (GFile *base_file, gpointer buf, fuse_fill_dir_t filler)
static gint static gint
vfs_readdir (const gchar *path, gpointer buf, fuse_fill_dir_t filler, off_t offset, vfs_readdir (const gchar *path, gpointer buf, fuse_fill_dir_t filler, off_t offset,
struct fuse_file_info *fi) struct fuse_file_info *fi, enum fuse_readdir_flags fl)
{ {
GFile *base_file; GFile *base_file;
gint result = 0; gint result = 0;
@ -1610,8 +1614,8 @@ vfs_readdir (const gchar *path, gpointer buf, fuse_fill_dir_t filler, off_t offs
/* Mount list */ /* Mount list */
filler (buf, ".", NULL, 0); filler (buf, ".", NULL, 0, 0);
filler (buf, "..", NULL, 0); filler (buf, "..", NULL, 0, 0);
mount_list_lock (); mount_list_lock ();
@ -1619,7 +1623,7 @@ vfs_readdir (const gchar *path, gpointer buf, fuse_fill_dir_t filler, off_t offs
{ {
MountRecord *mount_record = l->data; MountRecord *mount_record = l->data;
filler (buf, mount_record->name, NULL, 0); filler (buf, mount_record->name, NULL, 0, 0);
} }
mount_list_unlock (); mount_list_unlock ();
@ -1643,13 +1647,21 @@ vfs_readdir (const gchar *path, gpointer buf, fuse_fill_dir_t filler, off_t offs
} }
static gint static gint
vfs_rename (const gchar *old_path, const gchar *new_path) vfs_rename (const gchar *old_path, const gchar *new_path, unsigned int vfs_flags)
{ {
GFile *old_file; GFile *old_file;
GFile *new_file; GFile *new_file;
GFileCopyFlags flags = G_FILE_COPY_OVERWRITE;
GError *error = NULL; GError *error = NULL;
gint result = 0; gint result = 0;
/* Can not implement this flag because limitation of GFile. */
if (vfs_flags & RENAME_EXCHANGE)
return -EINVAL;
if (vfs_flags & RENAME_NOREPLACE)
flags = G_FILE_COPY_NONE;
g_debug ("vfs_rename: %s -> %s\n", old_path, new_path); g_debug ("vfs_rename: %s -> %s\n", old_path, new_path);
old_file = file_from_full_path (old_path); old_file = file_from_full_path (old_path);
@ -1665,7 +1677,7 @@ vfs_rename (const gchar *old_path, const gchar *new_path)
file_handle_close_stream (fh); file_handle_close_stream (fh);
} }
g_file_move (old_file, new_file, G_FILE_COPY_OVERWRITE, NULL, NULL, NULL, &error); g_file_move (old_file, new_file, flags, NULL, NULL, NULL, &error);
if (error) if (error)
{ {
@ -2072,6 +2084,15 @@ vfs_truncate (const gchar *path, off_t size)
return result; return result;
} }
static gint
vfs_truncate_dispatch (const gchar *path, off_t size, struct fuse_file_info *fi)
{
if (fi)
return vfs_ftruncate (path, size, fi);
return vfs_truncate (path, size);
}
static gint static gint
vfs_symlink (const gchar *path_old, const gchar *path_new) vfs_symlink (const gchar *path_old, const gchar *path_new)
{ {
@ -2178,7 +2199,7 @@ vfs_access (const gchar *path, gint mode)
} }
static gint static gint
vfs_utimens (const gchar *path, const struct timespec tv [2]) vfs_utimens (const gchar *path, const struct timespec tv [2], struct fuse_file_info *fi)
{ {
GFile *file; GFile *file;
GError *error = NULL; GError *error = NULL;
@ -2253,7 +2274,7 @@ vfs_utimens (const gchar *path, const struct timespec tv [2])
} }
static gint static gint
vfs_chmod (const gchar *path, mode_t mode) vfs_chmod (const gchar *path, mode_t mode, struct fuse_file_info *fi)
{ {
GFile *file; GFile *file;
GError *error = NULL; GError *error = NULL;
@ -2376,7 +2397,7 @@ register_fuse_cb (GVfsDBusMountTracker *proxy,
} }
static gpointer static gpointer
vfs_init (struct fuse_conn_info *conn) vfs_init (struct fuse_conn_info *conn, struct fuse_config *cfg)
{ {
GVfsDBusMountTracker *proxy; GVfsDBusMountTracker *proxy;
GError *error; GError *error;
@ -2452,11 +2473,7 @@ vfs_init (struct fuse_conn_info *conn)
conn->want |= FUSE_CAP_ATOMIC_O_TRUNC; conn->want |= FUSE_CAP_ATOMIC_O_TRUNC;
/* Prevent out-of-order readahead */ /* Prevent out-of-order readahead */
conn->async_read = 0; conn->want &= ~FUSE_CAP_ASYNC_READ;
/* Use up to a 64KiB write block size. Only has an effect if -o big_writes
* is given on the command-line. */
conn->max_write = 65536;
return NULL; return NULL;
} }
@ -2503,8 +2520,7 @@ static struct fuse_operations vfs_oper =
.unlink = vfs_unlink, .unlink = vfs_unlink,
.mkdir = vfs_mkdir, .mkdir = vfs_mkdir,
.rmdir = vfs_rmdir, .rmdir = vfs_rmdir,
.ftruncate = vfs_ftruncate, .truncate = vfs_truncate_dispatch,
.truncate = vfs_truncate,
.symlink = vfs_symlink, .symlink = vfs_symlink,
.access = vfs_access, .access = vfs_access,
.utimens = vfs_utimens, .utimens = vfs_utimens,
@ -2516,6 +2532,7 @@ static struct fuse_operations vfs_oper =
.getxattr = vfs_getxattr, .getxattr = vfs_getxattr,
.listxattr = vfs_listxattr, .listxattr = vfs_listxattr,
.removexattr = vfs_removexattr, .removexattr = vfs_removexattr,
.ftruncate = vfs_ftruncate,
#endif #endif
}; };
@ -2537,35 +2554,65 @@ gint
main (gint argc, gchar *argv []) main (gint argc, gchar *argv [])
{ {
struct fuse *fuse; struct fuse *fuse;
struct fuse_chan *ch;
struct fuse_session *se; struct fuse_session *se;
char *mountpoint;
int multithreaded;
int res; int res;
struct fuse_cmdline_opts opts;
struct fuse_args args = FUSE_ARGS_INIT (argc, argv);
fuse = fuse_setup (argc, argv, &vfs_oper, sizeof (vfs_oper), &mountpoint, if (fuse_opt_parse (&args, NULL, NULL, NULL) == -1)
&multithreaded, NULL /* user data */); return 1;
if (fuse_parse_cmdline (&args, &opts) != 0)
return 1;
if (opts.show_version)
{
printf ("%s\n", PACKAGE_STRING);
fuse_lowlevel_version ();
return 0;
}
if (opts.show_help)
{
printf ("usage: %s [options] <mountpoint>\n\n", argv[0]);
printf ("FUSE options:\n");
fuse_cmdline_help ();
return 0;
}
if (!opts.mountpoint)
{
fprintf (stderr, "error: no mountpoint specified\n");
return 1;
}
fuse = fuse_new (&args, &vfs_oper, sizeof (vfs_oper), NULL /* user data */);
if (fuse == NULL) if (fuse == NULL)
return 1; return 1;
if (multithreaded) if (fuse_mount (fuse, opts.mountpoint) != 0)
res = fuse_loop_mt (fuse); return 1;
else
res = fuse_loop (fuse); if (fuse_daemonize (opts.foreground) != 0)
return 1;
se = fuse_get_session (fuse); se = fuse_get_session (fuse);
ch = fuse_session_next_chan (se, NULL); if (fuse_set_signal_handlers (se) != 0)
return 1;
if (opts.singlethread)
res = fuse_loop (fuse);
else
res = fuse_loop_mt (fuse, opts.clone_fd);
/* Ignore new signals during exit procedure in order to terminate properly */ /* Ignore new signals during exit procedure in order to terminate properly */
set_custom_signal_handlers (SIG_IGN); set_custom_signal_handlers (SIG_IGN);
fuse_remove_signal_handlers (se); fuse_remove_signal_handlers (se);
fuse_unmount (mountpoint, ch); fuse_unmount (fuse);
fuse_destroy (fuse); fuse_destroy (fuse);
free (mountpoint); free (opts.mountpoint);
fuse_opt_free_args (&args);
if (res == -1) return res;
return 1;
return 0;
} }

View File

@ -58,6 +58,17 @@ create_proxy_for_icon (GVfsIcon *vfs_icon,
goto out; goto out;
connection = _g_dbus_connection_get_sync (mount_info->dbus_id, cancellable, &local_error); connection = _g_dbus_connection_get_sync (mount_info->dbus_id, cancellable, &local_error);
if (connection == NULL && !g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_dbus_error_strip_remote_error (local_error);
g_warning ("The peer-to-peer connection failed: %s. Falling back to the "
"session bus. Your application is probably missing "
"--filesystem=xdg-run/gvfsd privileges.", local_error->message);
g_clear_error (&local_error);
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, cancellable, &local_error);
}
if (connection == NULL) if (connection == NULL)
goto out; goto out;
@ -196,20 +207,9 @@ async_proxy_new_cb (GObject *source_object,
} }
static void static void
async_got_connection_cb (GDBusConnection *connection, async_construct_proxy (GDBusConnection *connection,
GError *io_error, AsyncPathCall *data)
gpointer callback_data)
{ {
AsyncPathCall *data = callback_data;
if (connection == NULL)
{
g_dbus_error_strip_remote_error (io_error);
g_task_return_error (data->task, io_error);
async_path_call_free (data);
return;
}
data->connection = g_object_ref (connection); data->connection = g_object_ref (connection);
gvfs_dbus_mount_proxy_new (connection, gvfs_dbus_mount_proxy_new (connection,
G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
@ -220,6 +220,62 @@ async_got_connection_cb (GDBusConnection *connection,
data); data);
} }
static void
bus_get_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
AsyncPathCall *data = user_data;
GDBusConnection *connection;
GError *error = NULL;
connection = g_bus_get_finish (res, &error);
if (connection == NULL)
{
g_dbus_error_strip_remote_error (error);
g_task_return_error (data->task, error);
async_path_call_free (data);
return;
}
async_construct_proxy (connection, data);
g_object_unref (connection);
}
static void
async_got_connection_cb (GDBusConnection *connection,
GError *io_error,
gpointer callback_data)
{
AsyncPathCall *data = callback_data;
if (connection == NULL)
{
g_dbus_error_strip_remote_error (io_error);
if (g_error_matches (io_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
{
g_task_return_error (data->task, g_error_copy (io_error));
async_path_call_free (data);
return;
}
g_warning ("The peer-to-peer connection failed: %s. Falling back to the "
"session bus. Your application is probably missing "
"--filesystem=xdg-run/gvfsd privileges.", io_error->message);
g_bus_get (G_BUS_TYPE_SESSION,
g_task_get_cancellable (data->task),
bus_get_cb,
data);
return;
}
async_construct_proxy (connection, data);
}
static void static void
async_got_mount_info (GMountInfo *mount_info, async_got_mount_info (GMountInfo *mount_info,
gpointer _data, gpointer _data,

View File

@ -245,7 +245,21 @@ g_vfs_decode_uri (const char *uri)
decoded->port = -1; decoded->port = -1;
} }
decoded->host = g_uri_unescape_segment (host_start, host_end, NULL); /* Let's use the IPv6 address without unescaping. This is needed in order
* to prevent g_uri_unescape_segment failures when zone identifier
* separated by the bare % as it is defined by RFC 4007 is used here. The
* zone identifier should contain just ASCII characters as per RFC 4007,
* so it doesn't need to be unescaped. I intentionally don't support here
* what is suggested by RFC 6874, which changes the separator to %25 and
* at the same time, it suggests that the bare % sign should still be
* accepted in user interfaces. Such a thing would make this too complex
* and lead to various problems (e.g. it would not be clear what separator
* should be used for g_file_get_uri function)...
*/
if (*host_start == '[')
decoded->host = g_strndup (host_start, host_end - host_start);
else
decoded->host = g_uri_unescape_segment (host_start, host_end, NULL);
hier_part_start = authority_end; hier_part_start = authority_end;
} }

View File

@ -33,7 +33,6 @@ sources = uri_parser_sources + uri_utils + files(
deps = [ deps = [
gio_unix_dep, gio_unix_dep,
libgvfscommon_dep,
libmetadata_dep, libmetadata_dep,
] ]

View File

@ -129,19 +129,16 @@ gvfs_setup_debug_handler (void)
gboolean gboolean
gvfs_is_ipv6 (const char *host) gvfs_is_ipv6 (const char *host)
{ {
const char *p = host;
g_return_val_if_fail (host != NULL, FALSE); g_return_val_if_fail (host != NULL, FALSE);
if (*p != '[') if (*host != '[' || host[strlen (host) - 1] != ']')
return FALSE;
while (++p)
if (!g_ascii_isxdigit (*p) && *p != ':')
break;
if (*p != ']' || *(p + 1) != '\0')
return FALSE; return FALSE;
return TRUE; return TRUE;
} }
gchar *
gvfs_get_socket_dir (void)
{
return g_build_filename (g_get_user_runtime_dir (), "gvfsd", NULL);
}

View File

@ -31,6 +31,7 @@ void gvfs_set_debug (gboolean debugging
void gvfs_setup_debug_handler (void); void gvfs_setup_debug_handler (void);
gboolean gvfs_is_ipv6 (const char *host); gboolean gvfs_is_ipv6 (const char *host);
gchar * gvfs_get_socket_dir (void);
G_END_DECLS G_END_DECLS

View File

@ -1,7 +1,7 @@
[Mount] [Mount]
Type=admin Type=admin
# Add a dummy argument after pkexec, or '/bin/sh -c' will eat the first argument in '$@' # Add a dummy argument after pkexec, or '/bin/sh -c' will eat the first argument in '$@'
Exec=/bin/sh -c 'pkexec @libexecdir@/gvfsd-admin "$@" --address $DBUS_SESSION_BUS_ADDRESS' gvfsd-admin Exec=/bin/sh -c 'pkexec @libexecdir@/gvfsd-admin "$@" --address $DBUS_SESSION_BUS_ADDRESS --dir $XDG_RUNTIME_DIR' gvfsd-admin
AutoMount=false AutoMount=false
DBusName=org.gtk.vfs.mountpoint_admin DBusName=org.gtk.vfs.mountpoint_admin
MountPerClient=true MountPerClient=true

View File

@ -1,7 +1,9 @@
[Unit] [Unit]
Description=Virtual filesystem service Description=Virtual filesystem service
PartOf=graphical-session.target
[Service] [Service]
ExecStart=@libexecdir@/gvfsd ExecStart=@libexecdir@/gvfsd
Type=dbus Type=dbus
BusName=org.gtk.vfs.Daemon BusName=org.gtk.vfs.Daemon
Slice=session.slice

View File

@ -1053,22 +1053,17 @@ g_vfs_afp_server_login (GVfsAfpServer *server,
gboolean aborted; gboolean aborted;
g_free (prompt); g_free (prompt);
g_clear_error (&err);
str = g_string_new (NULL); str = g_string_new (NULL);
if (err)
{
g_string_append_printf (str, "%s\n", err->message);
g_clear_error (&err);
}
/* create prompt */ /* create prompt */
if (initial_user) if (initial_user)
/* translators: %s here is the hostname */ /* Translators: the first %s is the username, the second the host name */
g_string_append_printf (str, _("Enter your password for the server “%s”."), server_name); g_string_append_printf (str, _("Authentication Required\nEnter password for “%s” on “%s”:"), initial_user, server_name);
else else
/* translators: %s here is the hostname */ /* Translators: %s here is the hostname */
g_string_append_printf (str, _("Enter your name and password for the server “%s”."), server_name); g_string_append_printf (str, _("Authentication Required\nEnter user and password for “%s”:"), server_name);
prompt = g_string_free (str, FALSE); prompt = g_string_free (str, FALSE);

View File

@ -524,6 +524,8 @@ get_thumbnail_attributes (const char *uri,
GChecksum *checksum; GChecksum *checksum;
char *filename; char *filename;
char *basename; char *basename;
const char *size_dirs[4] = { "xx-large", "x-large", "large", "normal" };
gsize i;
checksum = g_checksum_new (G_CHECKSUM_MD5); checksum = g_checksum_new (G_CHECKSUM_MD5);
g_checksum_update (checksum, (const guchar *) uri, strlen (uri)); g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
@ -531,34 +533,31 @@ get_thumbnail_attributes (const char *uri,
basename = g_strconcat (g_checksum_get_string (checksum), ".png", NULL); basename = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
g_checksum_free (checksum); g_checksum_free (checksum);
filename = g_build_filename (g_get_user_cache_dir (), for (i = 0; i < G_N_ELEMENTS (size_dirs); i++)
"thumbnails", "large", basename, {
NULL); filename = g_build_filename (g_get_user_cache_dir (),
"thumbnails", size_dirs[i], basename,
NULL);
if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
break;
if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) g_clear_pointer (&filename, g_free);
}
if (filename)
g_file_info_set_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH, filename); g_file_info_set_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH, filename);
else else
{ {
g_free (filename);
filename = g_build_filename (g_get_user_cache_dir (), filename = g_build_filename (g_get_user_cache_dir (),
"thumbnails", "normal", basename, "thumbnails", "fail",
"gnome-thumbnail-factory",
basename,
NULL); NULL);
if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
g_file_info_set_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH, filename); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED, TRUE);
else
{
g_free (filename);
filename = g_build_filename (g_get_user_cache_dir (),
"thumbnails", "fail",
"gnome-thumbnail-factory",
basename,
NULL);
if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED, TRUE);
}
} }
g_free (basename); g_free (basename);
g_free (filename); g_free (filename);
} }

View File

@ -95,6 +95,15 @@ check_permission (GVfsBackendAdmin *self,
invocation = dbus_job->invocation; invocation = dbus_job->invocation;
connection = g_dbus_method_invocation_get_connection (invocation); connection = g_dbus_method_invocation_get_connection (invocation);
credentials = g_dbus_connection_get_peer_credentials (connection); credentials = g_dbus_connection_get_peer_credentials (connection);
if (!credentials)
{
g_warning ("The admin backend doesn't work with the session bus "
"fallback. Your application is probably missing "
"--filesystem=xdg-run/gvfsd privileges.");
g_vfs_job_failed_literal (job, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
return FALSE;
}
pid = g_credentials_get_unix_pid (credentials, &error); pid = g_credentials_get_unix_pid (credentials, &error);
if (error != NULL) if (error != NULL)
@ -807,6 +816,36 @@ do_move (GVfsBackend *backend,
complete_job (job, error); complete_job (job, error);
} }
static void
do_copy (GVfsBackend *backend,
GVfsJobCopy *copy_job,
const char *source,
const char *destination,
GFileCopyFlags flags,
GFileProgressCallback progress_callback,
gpointer progress_callback_data)
{
GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
GVfsJob *job = G_VFS_JOB (copy_job);
GError *error = NULL;
GFile *src_file, *dst_file;
if (!check_permission (self, job))
return;
src_file = g_file_new_for_path (source);
dst_file = g_file_new_for_path (destination);
g_file_copy (src_file, dst_file, flags,
job->cancellable,
progress_callback, progress_callback_data,
&error);
g_object_unref (src_file);
g_object_unref (dst_file);
complete_job (job, error);
}
static void static void
do_pull (GVfsBackend *backend, do_pull (GVfsBackend *backend,
GVfsJobPull *pull_job, GVfsJobPull *pull_job,
@ -852,6 +891,40 @@ do_pull (GVfsBackend *backend,
complete_job (job, error); complete_job (job, error);
} }
static void
do_push (GVfsBackend *backend,
GVfsJobPush *push_job,
const char *destination,
const char *local_path,
GFileCopyFlags flags,
gboolean remove_source,
GFileProgressCallback progress_callback,
gpointer progress_callback_data)
{
GVfsBackendAdmin *self = G_VFS_BACKEND_ADMIN (backend);
GVfsJob *job = G_VFS_JOB (push_job);
GError *error = NULL;
GFile *src_file, *dst_file;
if (!check_permission (self, job))
return;
src_file = g_file_new_for_path (local_path);
dst_file = g_file_new_for_path (destination);
if (remove_source)
g_file_move (src_file, dst_file, flags, job->cancellable,
progress_callback, progress_callback_data, &error);
else
g_file_copy (src_file, dst_file, flags, job->cancellable,
progress_callback, progress_callback_data, &error);
g_object_unref (src_file);
g_object_unref (dst_file);
complete_job (job, error);
}
static void static void
do_query_settable_attributes (GVfsBackend *backend, do_query_settable_attributes (GVfsBackend *backend,
GVfsJobQueryAttributes *query_job, GVfsJobQueryAttributes *query_job,
@ -972,7 +1045,9 @@ g_vfs_backend_admin_class_init (GVfsBackendAdminClass * klass)
backend_class->set_attribute = do_set_attribute; backend_class->set_attribute = do_set_attribute;
backend_class->delete = do_delete; backend_class->delete = do_delete;
backend_class->move = do_move; backend_class->move = do_move;
backend_class->copy = do_copy;
backend_class->pull = do_pull; backend_class->pull = do_pull;
backend_class->push = do_push;
backend_class->query_settable_attributes = do_query_settable_attributes; backend_class->query_settable_attributes = do_query_settable_attributes;
backend_class->query_writable_namespaces = do_query_writable_namespaces; backend_class->query_writable_namespaces = do_query_writable_namespaces;
} }
@ -1028,8 +1103,10 @@ acquire_caps (uid_t uid)
} }
static char *session_address = NULL; static char *session_address = NULL;
static char *runtime_dir = NULL;
static GOptionEntry entries[] = { static GOptionEntry entries[] = {
{ "address", 0, 0, G_OPTION_ARG_STRING, &session_address, "DBus session address", NULL }, { "address", 0, 0, G_OPTION_ARG_STRING, &session_address, "DBus session address", NULL },
{ "dir", 0, 0, G_OPTION_ARG_STRING, &runtime_dir, "Runtime dir", NULL },
{ NULL } { NULL }
}; };
@ -1068,4 +1145,7 @@ g_vfs_backend_admin_pre_setup (int *argc,
acquire_caps (uid); acquire_caps (uid);
g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_address, TRUE); g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_address, TRUE);
if (runtime_dir)
g_setenv ("XDG_RUNTIME_DIR", runtime_dir, TRUE);
} }

View File

@ -574,7 +574,7 @@ g_vfs_backend_afc_mount (GVfsBackend *backend,
/* translators: /* translators:
* %s is the device name. 'Try again' is the caption of the button * %s is the device name. 'Try again' is the caption of the button
* shown in the dialog which is defined above. */ * shown in the dialog which is defined above. */
message = g_strdup_printf (_("The device “%s” is locked. Enter the passcode on the device and click “Try again”."), display_name); message = g_strdup_printf (_("Device Locked\nThe device “%s” is locked.\n\nEnter the passcode on the device and click “Try again”."), display_name);
} }
else if (lerr == LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING) else if (lerr == LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING)
{ {
@ -582,7 +582,7 @@ g_vfs_backend_afc_mount (GVfsBackend *backend,
* %s is the device name. 'Try again' is the caption of the button * %s is the device name. 'Try again' is the caption of the button
* shown in the dialog which is defined above. 'Trust' is the caption * shown in the dialog which is defined above. 'Trust' is the caption
* of the button shown in the device. */ * of the button shown in the device. */
message = g_strdup_printf (_("The device “%s” is not trusted yet. Select “Trust” on the device and click “Try again”."), display_name); message = g_strdup_printf (_("Untrusted Device\nThe device “%s” is not trusted yet.\n\nSelect “Trust” on the device and click “Try again”."), display_name);
} }
else else
g_assert_not_reached (); g_assert_not_reached ();
@ -2186,6 +2186,7 @@ g_vfs_backend_afc_query_fs_info (GVfsBackend *backend,
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "afc"); g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "afc");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
if (!self->connected) if (!self->connected)
{ {

View File

@ -1821,6 +1821,7 @@ try_query_fs_info (GVfsBackend *backend,
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "afp"); g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "afp");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE)) if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE))
vol_bitmap |= AFP_VOLUME_BITMAP_EXT_BYTES_TOTAL_BIT; vol_bitmap |= AFP_VOLUME_BITMAP_EXT_BYTES_TOTAL_BIT;

View File

@ -540,6 +540,7 @@ try_query_fs_info (GVfsBackend *backend,
{ {
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "afp"); g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "afp");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_NEVER);
g_vfs_job_succeeded (G_VFS_JOB (job)); g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE; return TRUE;
} }

View File

@ -1033,6 +1033,7 @@ try_query_fs_info (GVfsBackend *backend,
{ {
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "burn"); g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "burn");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL);
g_vfs_job_succeeded (G_VFS_JOB (job)); g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE; return TRUE;
} }

View File

@ -1424,6 +1424,7 @@ try_query_fs_info (GVfsBackend *backend,
{ {
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "computer"); g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "computer");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_NEVER);
g_vfs_job_succeeded (G_VFS_JOB (job)); g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE; return TRUE;
} }

File diff suppressed because it is too large Load Diff

View File

@ -842,6 +842,7 @@ try_query_fs_info (GVfsBackend *backend,
{ {
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "dns-sd"); g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "dns-sd");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_NEVER);
g_vfs_job_succeeded (G_VFS_JOB (job)); g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE; return TRUE;
} }

View File

@ -527,10 +527,10 @@ restart:
{ {
if (ftp->has_initial_user) if (ftp->has_initial_user)
/* Translators: the first %s is the username, the second the host name */ /* Translators: the first %s is the username, the second the host name */
prompt = g_strdup_printf (_("Enter password for %s on %s"), ftp->user, ftp->host_display_name); prompt = g_strdup_printf (_("Authentication Required\nEnter password for %s on %s”:"), ftp->user, ftp->host_display_name);
else else
/* translators: %s here is the hostname */ /* Translators: %s here is the hostname */
prompt = g_strdup_printf (_("Enter password for %s"), ftp->host_display_name); prompt = g_strdup_printf (_("Authentication Required\nEnter user and password for %s”:"), ftp->host_display_name);
} }
flags = G_ASK_PASSWORD_NEED_PASSWORD; flags = G_ASK_PASSWORD_NEED_PASSWORD;
@ -1533,47 +1533,6 @@ out:
g_vfs_ftp_task_done (&task); g_vfs_ftp_task_done (&task);
} }
static gssize
ftp_output_stream_splice (GOutputStream *output,
GInputStream *input,
goffset total_size,
GFileProgressCallback progress_callback,
gpointer progress_callback_data,
GCancellable *cancellable,
GError **error)
{
gssize n_read, n_written;
gssize bytes_copied;
char buffer[8192], *p;
bytes_copied = 0;
for (;;)
{
n_read = g_input_stream_read (input, buffer, sizeof (buffer), cancellable, error);
if (n_read == -1)
return -1;
if (n_read == 0)
break;
p = buffer;
while (n_read > 0)
{
n_written = g_output_stream_write (output, p, n_read, cancellable, error);
if (n_written == -1)
return -1;
p += n_written;
n_read -= n_written;
bytes_copied += n_written;
if (progress_callback)
progress_callback (bytes_copied, total_size, progress_callback_data);
}
}
return bytes_copied;
}
static void static void
do_pull_improve_error_message (GVfsFtpTask *task, do_pull_improve_error_message (GVfsFtpTask *task,
GFile *dest, GFile *dest,
@ -1657,6 +1616,15 @@ do_pull (GVfsBackend * backend,
src = g_vfs_ftp_file_new_from_gvfs (ftp, source); src = g_vfs_ftp_file_new_from_gvfs (ftp, source);
dest = g_file_new_for_path (local_path); dest = g_file_new_for_path (local_path);
if (remove_source && (flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE))
{
g_set_error_literal (&task.error,
G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
goto out;
}
/* If the source is a symlink, then it needs to be handled specially. */ /* If the source is a symlink, then it needs to be handled specially. */
if (flags & G_FILE_COPY_NOFOLLOW_SYMLINKS) if (flags & G_FILE_COPY_NOFOLLOW_SYMLINKS)
{ {
@ -1726,18 +1694,19 @@ do_pull (GVfsBackend * backend,
} }
input = g_io_stream_get_input_stream (g_vfs_ftp_connection_get_data_stream (task.conn)); input = g_io_stream_get_input_stream (g_vfs_ftp_connection_get_data_stream (task.conn));
ftp_output_stream_splice (output, gvfs_output_stream_splice (output,
input, input,
total_size, G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
progress_callback, total_size,
progress_callback_data, progress_callback,
task.cancellable, progress_callback_data,
&task.error); task.cancellable,
&task.error);
g_vfs_ftp_task_close_data_connection (&task); g_vfs_ftp_task_close_data_connection (&task);
g_vfs_ftp_task_receive (&task, 0, NULL); g_vfs_ftp_task_receive (&task, 0, NULL);
g_object_unref (output); g_object_unref (output);
if (remove_source) if (!g_vfs_ftp_task_is_in_error (&task) && remove_source)
{ {
g_vfs_ftp_task_send (&task, g_vfs_ftp_task_send (&task,
G_VFS_FTP_PASS_500, G_VFS_FTP_PASS_500,
@ -1759,6 +1728,7 @@ try_query_fs_info (GVfsBackend *backend,
{ {
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "ftp"); g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "ftp");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
g_vfs_job_succeeded (G_VFS_JOB (job)); g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE; return TRUE;
} }

View File

@ -49,17 +49,22 @@
#include "gvfsjobsetdisplayname.h" #include "gvfsjobsetdisplayname.h"
#include "gvfsjobwrite.h" #include "gvfsjobwrite.h"
#include "gvfsmonitor.h" #include "gvfsmonitor.h"
#include "gvfsdaemonutils.h"
struct _GVfsBackendGoogle struct _GVfsBackendGoogle
{ {
GVfsBackend parent; GVfsBackend parent;
GDataDocumentsService *service; GDataDocumentsService *service;
GDataEntry *root; GDataEntry *root;
GDataEntry *home;
GDataEntry *shared_with_me_dir;
GDataEntry *shared_drives_dir;
GHashTable *entries; /* gchar *entry_id -> GDataEntry */ GHashTable *entries; /* gchar *entry_id -> GDataEntry */
GHashTable *dir_entries; /* DirEntriesKey -> GDataEntry */ GHashTable *dir_entries; /* DirEntriesKey -> GDataEntry */
GHashTable *dir_timestamps; /* gchar *entry_id -> gint64 *timestamp */ GHashTable *dir_timestamps; /* gchar *entry_id -> gint64 *timestamp */
GHashTable *monitors; GHashTable *monitors;
GList *dir_collisions; GList *dir_collisions;
GList *shared_drives;
GRecMutex mutex; /* guards cache */ GRecMutex mutex; /* guards cache */
GoaClient *client; GoaClient *client;
gchar *account_identity; gchar *account_identity;
@ -79,14 +84,16 @@ G_DEFINE_TYPE(GVfsBackendGoogle, g_vfs_backend_google, G_VFS_TYPE_BACKEND)
#define CONTENT_TYPE_PREFIX_GOOGLE "application/vnd.google-apps" #define CONTENT_TYPE_PREFIX_GOOGLE "application/vnd.google-apps"
#define MAX_RESULTS 50
#define REBUILD_ENTRIES_TIMEOUT 60 /* s */ #define REBUILD_ENTRIES_TIMEOUT 60 /* s */
#define URI_PREFIX "https://www.googleapis.com/drive/v2/files/" #define URI_PREFIX "https://www.googleapis.com/drive/v2/files/"
#define SOURCE_ID_PROPERTY_KEY "GVfsSourceID" #define SOURCE_ID_PROPERTY_KEY "GVfsSourceID"
#define PARENT_ID_PROPERTY_KEY "GVfsParentID" #define PARENT_ID_PROPERTY_KEY "GVfsParentID"
#define ROOT_ID "GVfsRoot"
#define SHARED_WITH_ME_ID "GVfsSharedWithMe"
#define SHARED_DRIVES_ID "GVfsSharedDrives"
/* ---------------------------------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------------------------------- */
typedef struct typedef struct
@ -441,6 +448,12 @@ is_owner (GVfsBackendGoogle *self, GDataEntry *entry)
return FALSE; return FALSE;
} }
static gboolean
is_shared_with_me (GDataEntry *entry)
{
return gdata_documents_entry_get_shared_with_me_date (GDATA_DOCUMENTS_ENTRY (entry)) > 0;
}
/* ---------------------------------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------------------------------- */
static const gchar * static const gchar *
@ -717,6 +730,27 @@ insert_entry (GVfsBackendGoogle *self,
insert_entry_full (self, entry, TRUE); insert_entry_full (self, entry, TRUE);
} }
static void
insert_custom_entry (GVfsBackendGoogle *self,
GDataEntry *entry,
const gchar *parent_id)
{
DirEntriesKey *k;
const gchar *id;
const gchar *title;
id = gdata_entry_get_id (entry);
title = gdata_entry_get_title (entry);
g_hash_table_insert (self->entries, g_strdup (id), g_object_ref (entry));
k = dir_entries_key_new (id, parent_id);
g_hash_table_insert (self->dir_entries, k, g_object_ref (entry));
k = dir_entries_key_new (title, parent_id);
g_hash_table_insert (self->dir_entries, k, g_object_ref (entry));
}
static void static void
remove_entry_full (GVfsBackendGoogle *self, remove_entry_full (GVfsBackendGoogle *self,
GDataEntry *entry, GDataEntry *entry,
@ -733,6 +767,9 @@ remove_entry_full (GVfsBackendGoogle *self,
g_hash_table_remove (self->entries, id); g_hash_table_remove (self->entries, id);
if (is_shared_with_me (entry))
g_hash_table_remove (self->dir_timestamps, SHARED_WITH_ME_ID);
parent_ids = get_parent_ids (self, entry); parent_ids = get_parent_ids (self, entry);
for (ll = parent_ids; ll != NULL; ll = ll->next) for (ll = parent_ids; ll != NULL; ll = ll->next)
{ {
@ -875,6 +912,9 @@ is_entry_valid (GDataEntry *entry)
gint64 *timestamp; gint64 *timestamp;
timestamp = g_object_get_data (G_OBJECT (entry), "timestamp"); timestamp = g_object_get_data (G_OBJECT (entry), "timestamp");
if (timestamp == NULL)
return TRUE;
return (g_get_real_time () - *timestamp < REBUILD_ENTRIES_TIMEOUT * G_USEC_PER_SEC); return (g_get_real_time () - *timestamp < REBUILD_ENTRIES_TIMEOUT * G_USEC_PER_SEC);
} }
@ -883,6 +923,9 @@ is_dir_listing_valid (GVfsBackendGoogle *self, GDataEntry *parent)
{ {
gint64 *timestamp; gint64 *timestamp;
if (parent == self->root)
return TRUE;
timestamp = g_hash_table_lookup (self->dir_timestamps, gdata_entry_get_id (parent)); timestamp = g_hash_table_lookup (self->dir_timestamps, gdata_entry_get_id (parent));
if (timestamp != NULL) if (timestamp != NULL)
return (g_get_real_time () - *timestamp < REBUILD_ENTRIES_TIMEOUT * G_USEC_PER_SEC); return (g_get_real_time () - *timestamp < REBUILD_ENTRIES_TIMEOUT * G_USEC_PER_SEC);
@ -890,6 +933,48 @@ is_dir_listing_valid (GVfsBackendGoogle *self, GDataEntry *parent)
return FALSE; return FALSE;
} }
static void
rebuild_shared_drives_dir (GVfsBackendGoogle *self,
GCancellable *cancellable,
GError **error)
{
GDataAuthorizationDomain *auth_domain;
GList *l;
gint64 *timestamp;
auth_domain = gdata_documents_service_get_primary_authorization_domain ();
remove_dir (self, self->shared_drives_dir);
for (l = self->shared_drives; l != NULL; l = l->next)
{
GDataDocumentsDrive *drive = GDATA_DOCUMENTS_DRIVE (l->data);
GDataEntry *entry = NULL;
entry = gdata_service_query_single_entry (GDATA_SERVICE (self->service),
auth_domain,
gdata_entry_get_id (GDATA_ENTRY (drive)),
NULL,
GDATA_TYPE_DOCUMENTS_FOLDER,
cancellable,
error);
if (entry == NULL)
return;
/* Replace "My Drive" title by the real name of the Drive. */
gdata_entry_set_title (entry, gdata_documents_drive_get_name (drive));
insert_custom_entry (self, entry, SHARED_DRIVES_ID);
g_object_unref (entry);
}
timestamp = g_new (gint64, 1);
*timestamp = g_get_real_time ();
g_hash_table_insert (self->dir_timestamps, SHARED_DRIVES_ID, timestamp);
}
static void static void
rebuild_dir (GVfsBackendGoogle *self, rebuild_dir (GVfsBackendGoogle *self,
GDataEntry *parent, GDataEntry *parent,
@ -902,11 +987,20 @@ rebuild_dir (GVfsBackendGoogle *self,
gchar *search; gchar *search;
gchar *parent_id; gchar *parent_id;
if (parent == self->shared_drives_dir)
{
rebuild_shared_drives_dir (self, cancellable, error);
return;
}
/* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */ /* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
parent_id = g_strdup (gdata_entry_get_id (parent)); parent_id = g_strdup (gdata_entry_get_id (parent));
search = g_strdup_printf ("'%s' in parents", parent_id); if (parent == self->shared_with_me_dir)
query = gdata_documents_query_new_with_limits (search, 1, MAX_RESULTS); search = g_strdup ("sharedWithMe");
else
search = g_strdup_printf ("'%s' in parents", parent_id);
query = gdata_documents_query_new_with_limits (search, 1, G_MAXUINT);
gdata_documents_query_set_show_folders (query, TRUE); gdata_documents_query_set_show_folders (query, TRUE);
g_free (search); g_free (search);
@ -972,10 +1066,16 @@ resolve_child (GVfsBackendGoogle *self,
GDataEntry *entry; GDataEntry *entry;
const gchar *parent_id; const gchar *parent_id;
GError *local_error = NULL; GError *local_error = NULL;
gboolean is_shared_with_me_dir = (parent == self->shared_with_me_dir);
parent_id = gdata_entry_get_id (parent); parent_id = gdata_entry_get_id (parent);
k = dir_entries_key_new (basename, parent_id); k = dir_entries_key_new (basename, parent_id);
entry = g_hash_table_lookup (self->dir_entries, k);
if (is_shared_with_me_dir)
entry = g_hash_table_lookup (self->entries, basename);
else
entry = g_hash_table_lookup (self->dir_entries, k);
if ((entry == NULL && !is_dir_listing_valid (self, parent)) || if ((entry == NULL && !is_dir_listing_valid (self, parent)) ||
(entry != NULL && !is_entry_valid (entry))) (entry != NULL && !is_entry_valid (entry)))
{ {
@ -986,7 +1086,10 @@ resolve_child (GVfsBackendGoogle *self,
goto out; goto out;
} }
entry = g_hash_table_lookup (self->dir_entries, k); if (is_shared_with_me_dir)
entry = g_hash_table_lookup (self->entries, basename);
else
entry = g_hash_table_lookup (self->dir_entries, k);
} }
if (entry == NULL) if (entry == NULL)
@ -1036,8 +1139,26 @@ resolve (GVfsBackendGoogle *self,
ret_val = resolve_child (self, parent, basename, cancellable, &local_error); ret_val = resolve_child (self, parent, basename, cancellable, &local_error);
if (ret_val == NULL) if (ret_val == NULL)
{ {
g_propagate_error (error, local_error); /* This fallback provides volatile entries for URIs which was used
goto out; * before My Drive folder was added in the root. */
if (parent == self->root)
{
g_clear_error (&local_error);
ret_val = resolve_child (self, self->home, basename, cancellable, &local_error);
if (ret_val != NULL && out_path != NULL)
{
gchar *tmp;
tmp = g_build_path ("/", *out_path, gdata_entry_get_id (self->home), NULL);
g_free (*out_path);
*out_path = tmp;
}
}
if (ret_val == NULL)
{
g_propagate_error (error, local_error);
goto out;
}
} }
if (out_path != NULL) if (out_path != NULL)
@ -1213,6 +1334,10 @@ build_file_info (GVfsBackendGoogle *self,
gint64 ctime; gint64 ctime;
gint64 mtime; gint64 mtime;
gsize i; gsize i;
gboolean is_shared_with_me_dir = (entry == self->shared_with_me_dir);
gboolean is_home = (entry == self->home);
gboolean is_shared_drives_dir = (entry == self->shared_drives_dir);
gboolean can_edit;
if (GDATA_IS_DOCUMENTS_FOLDER (entry)) if (GDATA_IS_DOCUMENTS_FOLDER (entry))
is_folder = TRUE; is_folder = TRUE;
@ -1226,7 +1351,11 @@ build_file_info (GVfsBackendGoogle *self,
symlink_name = g_path_get_basename (filename); symlink_name = g_path_get_basename (filename);
} }
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, !is_root); /* TODO: It is not always possible to rename, delete, or list children.
* However, the proper implementation of gdata_documents_entry_can_rename/
* _delete/_list_children would require port to Google Drive API v3. */
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME,
!is_root && !is_home && !is_shared_with_me_dir && !is_shared_drives_dir);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, is_folder); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, is_folder);
@ -1234,7 +1363,13 @@ build_file_info (GVfsBackendGoogle *self,
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_VOLATILE, is_symlink); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STANDARD_IS_VOLATILE, is_symlink);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, !is_root); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE,
!is_root && !is_home && !is_shared_with_me_dir && !is_shared_drives_dir);
can_edit = gdata_documents_entry_can_edit (GDATA_DOCUMENTS_ENTRY (entry));
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
!is_root && !is_shared_with_me_dir && !is_shared_drives_dir && can_edit);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ, TRUE);
if (is_folder) if (is_folder)
{ {
@ -1286,10 +1421,28 @@ build_file_info (GVfsBackendGoogle *self,
g_file_info_set_content_type (info, content_type); g_file_info_set_content_type (info, content_type);
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, content_type); g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, content_type);
icon = g_content_type_get_icon (content_type); if (is_home)
g_file_info_set_icon (info, icon); {
icon = g_themed_icon_new_with_default_fallbacks ("user-home");
symbolic_icon = g_themed_icon_new_with_default_fallbacks ("user-home-symbolic");
}
else if (is_shared_with_me_dir)
{
icon = g_themed_icon_new_with_default_fallbacks ("folder-publicshare");
symbolic_icon = g_themed_icon_new_with_default_fallbacks ("folder-publicshare-symbolic");
}
else if (is_shared_drives_dir)
{
icon = g_themed_icon_new_with_default_fallbacks ("folder-remote");
symbolic_icon = g_themed_icon_new_with_default_fallbacks ("folder-remote-symbolic");
}
else
{
icon = g_content_type_get_icon (content_type);
symbolic_icon = g_content_type_get_symbolic_icon (content_type);
}
symbolic_icon = g_content_type_get_symbolic_icon (content_type); g_file_info_set_icon (info, icon);
g_file_info_set_symbolic_icon (info, symbolic_icon); g_file_info_set_symbolic_icon (info, symbolic_icon);
g_object_unref (icon); g_object_unref (icon);
@ -1298,13 +1451,12 @@ build_file_info (GVfsBackendGoogle *self,
g_file_info_set_file_type (info, file_type); g_file_info_set_file_type (info, file_type);
if (is_root)
goto out;
id = gdata_entry_get_id (entry); id = gdata_entry_get_id (entry);
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILE, id); g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILE, id);
if (is_symlink) if (is_root)
name = "/";
else if (is_symlink)
name = symlink_name; name = symlink_name;
else else
name = id; name = id;
@ -1315,6 +1467,9 @@ build_file_info (GVfsBackendGoogle *self,
g_file_info_set_display_name (info, title); g_file_info_set_display_name (info, title);
g_file_info_set_edit_name (info, title); g_file_info_set_edit_name (info, title);
if (is_root || is_home || is_shared_with_me_dir || is_shared_drives_dir)
goto out;
copy_name = generate_copy_name (self, entry, entry_path); copy_name = generate_copy_name (self, entry, entry_path);
/* Sanitize copy-name by replacing slashes with dashes. This is /* Sanitize copy-name by replacing slashes with dashes. This is
@ -1461,6 +1616,14 @@ g_vfs_backend_google_copy (GVfsBackend *_self,
goto out; goto out;
} }
if (source_entry == self->root || source_parent == self->root || source_parent == self->shared_drives_dir ||
destination_parent == self->root || destination_parent == self->shared_with_me_dir ||
destination_parent == self->shared_drives_dir)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out;
}
id = gdata_entry_get_id (source_entry); id = gdata_entry_get_id (source_entry);
title = gdata_entry_get_title (source_entry); title = gdata_entry_get_title (source_entry);
source_parent_id = gdata_entry_get_id (source_parent); source_parent_id = gdata_entry_get_id (source_parent);
@ -1875,6 +2038,14 @@ g_vfs_backend_google_move (GVfsBackend *_self,
goto out; goto out;
} }
if (source_entry == self->root || source_parent == self->root || source_parent == self->shared_drives_dir ||
destination_parent == self->root || destination_parent == self->shared_with_me_dir ||
destination_parent == self->shared_drives_dir)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out;
}
source_id = gdata_entry_get_id (source_entry); source_id = gdata_entry_get_id (source_entry);
source_parent_id = gdata_entry_get_id (source_parent); source_parent_id = gdata_entry_get_id (source_parent);
destination_parent_id = gdata_entry_get_id (destination_parent); destination_parent_id = gdata_entry_get_id (destination_parent);
@ -2288,7 +2459,18 @@ g_vfs_backend_google_delete (GVfsBackend *_self,
/* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */ /* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
id = g_strdup (gdata_entry_get_id (entry)); id = g_strdup (gdata_entry_get_id (entry));
if (GDATA_IS_DOCUMENTS_FOLDER (entry)) parent = resolve_dir (self, filename, cancellable, NULL, NULL, &error);
if (error != NULL)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
g_error_free (error);
goto out;
}
/* The G_IO_ERROR_NOT_EMPTY error is not intentionally returned for folders in
* Shared with me folder, because the recursive delete would not work, or could
* really remove the files from the original folder also for the owner... */
if (GDATA_IS_DOCUMENTS_FOLDER (entry) && parent != self->shared_with_me_dir)
{ {
GHashTableIter iter; GHashTableIter iter;
DirEntriesKey *key; DirEntriesKey *key;
@ -2318,17 +2500,10 @@ g_vfs_backend_google_delete (GVfsBackend *_self,
} }
} }
parent = resolve_dir (self, filename, cancellable, NULL, NULL, &error);
if (error != NULL)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
g_error_free (error);
goto out;
}
g_debug (" entry path: %s\n", entry_path); g_debug (" entry path: %s\n", entry_path);
if (entry == self->root) if (entry == self->root || entry == self->home || entry == self->shared_with_me_dir ||
entry == self->shared_drives_dir || parent == self->shared_drives_dir)
{ {
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported")); g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out; goto out;
@ -2342,7 +2517,45 @@ g_vfs_backend_google_delete (GVfsBackend *_self,
parent_ids = get_parent_ids (self, entry); parent_ids = get_parent_ids (self, entry);
parent_ids_len = g_list_length (parent_ids); parent_ids_len = g_list_length (parent_ids);
if (parent_ids_len > 1 || !is_owner (self, GDATA_ENTRY (entry)))
/* The files in Shared with me folder doesn't have a parent and also we don't
* have enough permissions to physically delete them. But they can be removed
* by removal of our permissions... */
if (parent == self->shared_with_me_dir)
{
GDataFeed *acl_feed;
acl_feed = gdata_access_handler_get_rules (GDATA_ACCESS_HANDLER (entry),
GDATA_SERVICE (self->service),
cancellable,
NULL, NULL, &error);
if (error == NULL)
{
GDataGoaAuthorizer *goa_authorizer;
GoaAccount *account;
const gchar *account_identity;
GDataAuthorizationDomain *auth_domain;
GList *entries;
goa_authorizer = GDATA_GOA_AUTHORIZER (gdata_service_get_authorizer (GDATA_SERVICE (self->service)));
account = goa_object_peek_account (gdata_goa_authorizer_get_goa_object (goa_authorizer));
account_identity = goa_account_get_identity (account);
auth_domain = gdata_documents_service_get_primary_authorization_domain ();
for (entries = gdata_feed_get_entries (acl_feed); entries != NULL; entries = entries->next)
{
const gchar *scope_value = NULL;
GDataAccessRule *rule = GDATA_ACCESS_RULE (entries->data);
gdata_access_rule_get_scope (rule, NULL, &scope_value);
if (g_strcmp0 (scope_value, account_identity) == 0)
gdata_service_delete_entry (GDATA_SERVICE (self->service), auth_domain, GDATA_ENTRY (rule), NULL, &error);
}
g_object_unref (acl_feed);
}
}
else if (parent_ids_len > 1 || !is_owner (self, GDATA_ENTRY (entry)))
{ {
/* gdata_documents_service_remove_entry_from_folder () returns the /* gdata_documents_service_remove_entry_from_folder () returns the
* updated entry variable provided as argument with an increased ref. * updated entry variable provided as argument with an increased ref.
@ -2400,10 +2613,12 @@ g_vfs_backend_google_enumerate (GVfsBackend *_self,
GVfsBackendGoogle *self = G_VFS_BACKEND_GOOGLE (_self); GVfsBackendGoogle *self = G_VFS_BACKEND_GOOGLE (_self);
GCancellable *cancellable = G_VFS_JOB (job)->cancellable; GCancellable *cancellable = G_VFS_JOB (job)->cancellable;
GDataEntry *entry; GDataEntry *entry;
GDataEntry *child;
GError *error; GError *error;
GHashTableIter iter; GHashTableIter iter;
char *parent_path; char *parent_path;
char *id = NULL; char *id = NULL;
gboolean is_shared_with_me_dir;
g_rec_mutex_lock (&self->mutex); g_rec_mutex_lock (&self->mutex);
g_debug ("+ enumerate: %s\n", filename); g_debug ("+ enumerate: %s\n", filename);
@ -2439,18 +2654,20 @@ g_vfs_backend_google_enumerate (GVfsBackend *_self,
/* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */ /* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
id = g_strdup (gdata_entry_get_id (entry)); id = g_strdup (gdata_entry_get_id (entry));
is_shared_with_me_dir = (entry == self->shared_with_me_dir);
g_hash_table_iter_init (&iter, self->entries); g_hash_table_iter_init (&iter, self->entries);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &entry)) while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &child))
{ {
DirEntriesKey *k; DirEntriesKey *k;
GDataEntry *child;
gchar *child_id; gchar *child_id;
/* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */ /* g_strdup() is necessary to prevent segfault because gdata_entry_get_id() calls g_free() */
child_id = g_strdup (gdata_entry_get_id (entry)); child_id = g_strdup (gdata_entry_get_id (child));
k = dir_entries_key_new (child_id, id); k = dir_entries_key_new (child_id, id);
if ((child = g_hash_table_lookup (self->dir_entries, k)) != NULL) if ((is_shared_with_me_dir && is_shared_with_me (child)) ||
(!is_shared_with_me_dir && g_hash_table_lookup (self->dir_entries, k) != NULL))
{ {
GFileInfo *info; GFileInfo *info;
gchar *entry_path; gchar *entry_path;
@ -2468,7 +2685,7 @@ g_vfs_backend_google_enumerate (GVfsBackend *_self,
info = g_file_info_new (); info = g_file_info_new ();
entry_path = g_build_path ("/", parent_path, child_id, NULL); entry_path = g_build_path ("/", parent_path, child_id, NULL);
child_filename = g_build_filename (filename, child_id, NULL); child_filename = g_build_filename (filename, child_id, NULL);
build_file_info (self, entry, flags, info, matcher, child_filename, entry_path, NULL); build_file_info (self, child, flags, info, matcher, child_filename, entry_path, NULL);
g_vfs_job_enumerate_add_info (job, info); g_vfs_job_enumerate_add_info (job, info);
g_object_unref (info); g_object_unref (info);
g_free (entry_path); g_free (entry_path);
@ -2619,6 +2836,48 @@ g_vfs_backend_google_make_directory (GVfsBackend *_self,
/* ---------------------------------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------------------------------- */
static GList *
query_shared_drives (GVfsBackendGoogle *self,
GCancellable *cancellable,
GError **error)
{
GDataDocumentsDriveQuery *query;
GList *shared_drives = NULL;
query = gdata_documents_drive_query_new (NULL);
while (TRUE)
{
GDataDocumentsFeed *feed;
GList *entries;
feed = gdata_documents_service_query_drives (self->service,
query,
cancellable,
NULL,
NULL,
error);
if (feed == NULL)
break;
entries = gdata_feed_get_entries (GDATA_FEED (feed));
if (entries == NULL)
{
g_object_unref (feed);
break;
}
shared_drives = g_list_concat (shared_drives,
g_list_copy_deep (entries, (GCopyFunc) g_object_ref, NULL));
gdata_query_next_page (GDATA_QUERY (query));
g_object_unref (feed);
}
g_clear_object (&query);
return shared_drives;
}
static void static void
g_vfs_backend_google_mount (GVfsBackend *_self, g_vfs_backend_google_mount (GVfsBackend *_self,
GVfsJobMount *job, GVfsJobMount *job,
@ -2687,8 +2946,11 @@ g_vfs_backend_google_mount (GVfsBackend *_self,
auth_domain = gdata_documents_service_get_primary_authorization_domain (); auth_domain = gdata_documents_service_get_primary_authorization_domain ();
self->root = GDATA_ENTRY (gdata_documents_folder_new (ROOT_ID));
gdata_entry_set_title (self->root, self->account_identity);
error = NULL; error = NULL;
self->root = gdata_service_query_single_entry (GDATA_SERVICE (self->service), self->home = gdata_service_query_single_entry (GDATA_SERVICE (self->service),
auth_domain, auth_domain,
"root", "root",
NULL, NULL,
@ -2702,6 +2964,32 @@ g_vfs_backend_google_mount (GVfsBackend *_self,
g_error_free (error); g_error_free (error);
goto out; goto out;
} }
insert_custom_entry (self, self->home, ROOT_ID);
self->shared_with_me_dir = GDATA_ENTRY (gdata_documents_folder_new (SHARED_WITH_ME_ID));
/* Translators: This is the "Shared with me" folder on https://drive.google.com. */
gdata_entry_set_title (self->shared_with_me_dir, _("Shared with me"));
insert_custom_entry (self, self->shared_with_me_dir, ROOT_ID);
self->shared_drives = query_shared_drives (self, cancellable, &error);
if (error != NULL)
{
sanitize_error (&error);
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
g_error_free (error);
goto out;
}
if (self->shared_drives)
{
self->shared_drives_dir = GDATA_ENTRY (gdata_documents_folder_new (SHARED_DRIVES_ID));
/* Translators: This is the "Shared drives" folder on https://drive.google.com. */
gdata_entry_set_title (self->shared_drives_dir, _("Shared drives"));
insert_custom_entry (self, self->shared_drives_dir, ROOT_ID);
}
/* TODO: Make it work with GOA volume monitor resp. shadow mounts. */
g_vfs_backend_set_default_location (_self, gdata_entry_get_id (self->home));
g_vfs_backend_set_mount_spec (_self, spec); g_vfs_backend_set_mount_spec (_self, spec);
g_vfs_backend_set_display_name (_self, self->account_identity); g_vfs_backend_set_display_name (_self, self->account_identity);
@ -2772,11 +3060,16 @@ g_vfs_backend_google_push (GVfsBackend *_self,
gchar *entry_path = NULL; gchar *entry_path = NULL;
gchar *parent_path = NULL; gchar *parent_path = NULL;
gchar *local_file_title = NULL; gchar *local_file_title = NULL;
goffset size;
g_rec_mutex_lock (&self->mutex); g_rec_mutex_lock (&self->mutex);
g_debug ("+ push: %s -> %s, %d\n", local_path, destination, flags); g_debug ("+ push: %s -> %s, %d\n", local_path, destination, flags);
if (remove_source && (flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE))
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out;
}
if (flags & G_FILE_COPY_BACKUP) if (flags & G_FILE_COPY_BACKUP)
{ {
/* Return G_IO_ERROR_NOT_SUPPORTED instead of /* Return G_IO_ERROR_NOT_SUPPORTED instead of
@ -2793,7 +3086,8 @@ g_vfs_backend_google_push (GVfsBackend *_self,
info = g_file_query_info (local_file, info = g_file_query_info (local_file,
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE"," G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE","
G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME"," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME","
G_FILE_ATTRIBUTE_STANDARD_TYPE, G_FILE_ATTRIBUTE_STANDARD_TYPE","
G_FILE_ATTRIBUTE_STANDARD_SIZE,
G_FILE_QUERY_INFO_NONE, G_FILE_QUERY_INFO_NONE,
cancellable, cancellable,
&error); &error);
@ -2815,6 +3109,12 @@ g_vfs_backend_google_push (GVfsBackend *_self,
goto out; goto out;
} }
if (destination_parent == self->root || destination_parent == self->shared_with_me_dir || destination_parent == self->shared_drives_dir)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out;
}
existing_entry = resolve_child (self, destination_parent, destination_basename, cancellable, NULL); existing_entry = resolve_child (self, destination_parent, destination_basename, cancellable, NULL);
if (existing_entry != NULL) if (existing_entry != NULL)
{ {
@ -2969,11 +3269,14 @@ g_vfs_backend_google_push (GVfsBackend *_self,
} }
error = NULL; error = NULL;
g_output_stream_splice (G_OUTPUT_STREAM (ostream), gvfs_output_stream_splice (G_OUTPUT_STREAM (ostream),
G_INPUT_STREAM (istream), G_INPUT_STREAM (istream),
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
cancellable, g_file_info_get_size (info),
&error); progress_callback,
progress_callback_data,
cancellable,
&error);
if (error != NULL) if (error != NULL)
{ {
g_vfs_job_failed_from_error (G_VFS_JOB (job), error); g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
@ -3012,8 +3315,6 @@ g_vfs_backend_google_push (GVfsBackend *_self,
} }
} }
size = gdata_documents_entry_get_file_size (GDATA_DOCUMENTS_ENTRY (new_document));
g_vfs_job_progress_callback (size, size, job);
g_vfs_job_succeeded (G_VFS_JOB (job)); g_vfs_job_succeeded (G_VFS_JOB (job));
out: out:
@ -3089,6 +3390,7 @@ g_vfs_backend_google_query_fs_info (GVfsBackend *_self,
type = g_mount_spec_get_type (spec); type = g_mount_spec_get_type (spec);
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, type); g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, type);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE) || if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE) ||
g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_FREE) || g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_FILESYSTEM_FREE) ||
@ -3441,6 +3743,7 @@ g_vfs_backend_google_set_display_name (GVfsBackend *_self,
GDataAuthorizationDomain *auth_domain; GDataAuthorizationDomain *auth_domain;
GDataEntry *entry; GDataEntry *entry;
GDataEntry *new_entry = NULL; GDataEntry *new_entry = NULL;
GDataEntry *parent;
GError *error; GError *error;
gchar *entry_path = NULL; gchar *entry_path = NULL;
@ -3458,7 +3761,15 @@ g_vfs_backend_google_set_display_name (GVfsBackend *_self,
g_debug (" entry path: %s\n", entry_path); g_debug (" entry path: %s\n", entry_path);
if (entry == self->root) parent = resolve_dir (self, filename, cancellable, NULL, NULL, &error);
if (error != NULL)
{
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
g_error_free (error);
goto out;
}
if (entry == self->root || entry == self->home || entry == self->shared_with_me_dir || parent == self->shared_drives_dir)
{ {
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported")); g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out; goto out;
@ -3536,6 +3847,12 @@ g_vfs_backend_google_create (GVfsBackend *_self,
g_debug (" parent path: %s\n", parent_path); g_debug (" parent path: %s\n", parent_path);
if (parent == self->root || parent == self->shared_with_me_dir || parent == self->shared_drives_dir)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out;
}
existing_entry = resolve_child (self, parent, basename, cancellable, NULL); existing_entry = resolve_child (self, parent, basename, cancellable, NULL);
if (existing_entry != NULL) if (existing_entry != NULL)
{ {
@ -3645,6 +3962,12 @@ g_vfs_backend_google_replace (GVfsBackend *_self,
g_debug (" parent path: %s\n", parent_path); g_debug (" parent path: %s\n", parent_path);
if (parent == self->root || parent == self->shared_with_me_dir || parent == self->shared_drives_dir)
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Operation not supported"));
goto out;
}
existing_entry = resolve_child (self, parent, basename, cancellable, NULL); existing_entry = resolve_child (self, parent, basename, cancellable, NULL);
if (existing_entry != NULL) if (existing_entry != NULL)
{ {
@ -3875,6 +4198,10 @@ g_vfs_backend_google_dispose (GObject *_self)
g_clear_object (&self->service); g_clear_object (&self->service);
g_clear_object (&self->root); g_clear_object (&self->root);
g_clear_object (&self->home);
g_clear_object (&self->shared_with_me_dir);
g_clear_object (&self->shared_drives_dir);
g_clear_list (&self->shared_drives, g_object_unref);
g_clear_object (&self->client); g_clear_object (&self->client);
g_clear_pointer (&self->entries, g_hash_table_unref); g_clear_pointer (&self->entries, g_hash_table_unref);
g_clear_pointer (&self->dir_entries, g_hash_table_unref); g_clear_pointer (&self->dir_entries, g_hash_table_unref);

View File

@ -3243,6 +3243,14 @@ do_pull (GVfsBackend *backend,
split_filename_with_ignore_prefix (gphoto2_backend, source, &dir, &name); split_filename_with_ignore_prefix (gphoto2_backend, source, &dir, &name);
if (remove_source && (flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE))
{
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,
G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
goto out;
}
if (remove_source && !gphoto2_backend->can_delete) if (remove_source && !gphoto2_backend->can_delete)
{ {
g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR,

View File

@ -1,6 +1,7 @@
/* GIO - GLib Input, Output and Streaming Library /* GIO - GLib Input, Output and Streaming Library
* *
* Copyright (C) 2008 Red Hat, Inc. * Copyright (C) 2008 Red Hat, Inc.
* Copyright (C) 2021 Igalia S.L.
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -51,7 +52,7 @@
#include "gvfsdaemonprotocol.h" #include "gvfsdaemonprotocol.h"
#include "gvfsdaemonutils.h" #include "gvfsdaemonutils.h"
static SoupSession *the_session; static SoupSession *the_session = NULL;
G_DEFINE_TYPE (GVfsBackendHttp, g_vfs_backend_http, G_VFS_TYPE_BACKEND) G_DEFINE_TYPE (GVfsBackendHttp, g_vfs_backend_http, G_VFS_TYPE_BACKEND)
@ -63,7 +64,7 @@ g_vfs_backend_http_finalize (GObject *object)
backend = G_VFS_BACKEND_HTTP (object); backend = G_VFS_BACKEND_HTTP (object);
if (backend->mount_base) if (backend->mount_base)
soup_uri_free (backend->mount_base); g_uri_unref (backend->mount_base);
g_object_unref (backend->session); g_object_unref (backend->session);
@ -77,18 +78,89 @@ g_vfs_backend_http_init (GVfsBackendHttp *backend)
{ {
g_vfs_backend_set_user_visible (G_VFS_BACKEND (backend), FALSE); g_vfs_backend_set_user_visible (G_VFS_BACKEND (backend), FALSE);
backend->session = g_object_ref (the_session); /* attempt to use libsoup's default values */
backend->session = g_object_ref (http_try_init_session (-1, -1));
} }
/* ************************************************************************* */ /* ************************************************************************* */
/* public utility functions */ /* public utility functions */
SoupURI * GUri *
http_backend_get_mount_base (GVfsBackend *backend) http_backend_get_mount_base (GVfsBackend *backend)
{ {
return G_VFS_BACKEND_HTTP (backend)->mount_base; return G_VFS_BACKEND_HTTP (backend)->mount_base;
} }
#define DEBUG_MAX_BODY_SIZE (100 * 1024 * 1024)
/* initializes the session singleton; if max_conns is lower than 0, the
* libsoup defaults are used for max-conns and max-conns-per-host, this
* is called in the instance constructor, so if they are to be overridden,
* all one has to do is make sure to call it with the desired values before
* any instance is created (most likely in the class constructor of the
* derived class, see dav backend)
*/
SoupSession *
http_try_init_session (gint max_conns, gint max_conns_per_host)
{
const char *debug;
SoupSessionFeature *cookie_jar;
if (the_session)
return the_session;
/* Initialize the SoupSession, common to all backend instances */
if (max_conns < 0)
the_session = soup_session_new_with_options ("user-agent",
"gvfs/" VERSION, NULL);
else
the_session = soup_session_new_with_options ("user-agent",
"gvfs/" VERSION,
"max-conns",
max_conns,
"max-conns-per-host",
max_conns_per_host,
NULL);
/* Cookie handling - stored temporarlly in memory, mostly useful for
* authentication in WebDAV. */
cookie_jar = g_object_new (SOUP_TYPE_COOKIE_JAR, NULL);
soup_session_add_feature (the_session, cookie_jar);
g_object_unref (cookie_jar);
/* Send Accept-Language header (see bug 166795) */
soup_session_set_accept_language_auto (the_session, TRUE);
/* Prevent connection timeouts during long operations like COPY. */
soup_session_set_timeout (the_session, 0);
/* Logging */
debug = g_getenv ("GVFS_HTTP_DEBUG");
if (debug)
{
SoupLogger *logger;
SoupLoggerLogLevel level;
if (g_ascii_strcasecmp (debug, "all") == 0 ||
g_ascii_strcasecmp (debug, "body") == 0)
level = SOUP_LOGGER_LOG_BODY;
else if (g_ascii_strcasecmp (debug, "header") == 0)
level = SOUP_LOGGER_LOG_HEADERS;
else
level = SOUP_LOGGER_LOG_MINIMAL;
logger = soup_logger_new (level);
g_object_set (G_OBJECT (logger),
"max-body-size",
DEBUG_MAX_BODY_SIZE,
NULL);
soup_session_add_feature (the_session, SOUP_SESSION_FEATURE (logger));
g_object_unref (logger);
}
return the_session;
}
char * char *
http_path_get_basename (const char *path) http_path_get_basename (const char *path)
{ {
@ -138,7 +210,7 @@ http_uri_get_basename (const char *uri_str)
basename = http_path_get_basename (uri_str); basename = http_path_get_basename (uri_str);
decoded = soup_uri_decode (basename); decoded = g_uri_unescape_string (basename, NULL);
g_free (basename); g_free (basename);
return decoded; return decoded;
@ -149,13 +221,6 @@ http_error_code_from_status (guint status)
{ {
switch (status) { switch (status) {
case SOUP_STATUS_CANT_RESOLVE:
case SOUP_STATUS_CANT_RESOLVE_PROXY:
return G_IO_ERROR_HOST_NOT_FOUND;
case SOUP_STATUS_CANCELLED:
return G_IO_ERROR_CANCELLED;
case SOUP_STATUS_UNAUTHORIZED: case SOUP_STATUS_UNAUTHORIZED:
case SOUP_STATUS_PAYMENT_REQUIRED: case SOUP_STATUS_PAYMENT_REQUIRED:
case SOUP_STATUS_FORBIDDEN: case SOUP_STATUS_FORBIDDEN:
@ -184,45 +249,27 @@ http_error_code_from_status (guint status)
void void
http_job_failed (GVfsJob *job, SoupMessage *msg) http_job_failed (GVfsJob *job, SoupMessage *msg)
{ {
switch (msg->status_code) { switch (soup_message_get_status(msg)) {
case SOUP_STATUS_NOT_FOUND: case SOUP_STATUS_NOT_FOUND:
g_vfs_job_failed_literal (job, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, g_vfs_job_failed_literal (job, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
msg->reason_phrase); soup_message_get_reason_phrase(msg));
break; break;
case SOUP_STATUS_UNAUTHORIZED: case SOUP_STATUS_UNAUTHORIZED:
case SOUP_STATUS_PAYMENT_REQUIRED: case SOUP_STATUS_PAYMENT_REQUIRED:
case SOUP_STATUS_FORBIDDEN: case SOUP_STATUS_FORBIDDEN:
g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED, g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
_("HTTP Client Error: %s"), msg->reason_phrase); _("HTTP Client Error: %s"),
soup_message_get_reason_phrase(msg));
break; break;
default: default:
g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED, g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
_("HTTP Error: %s"), msg->reason_phrase); _("HTTP Error: %s"),
soup_message_get_reason_phrase(msg));
} }
} }
guint
http_backend_send_message (GVfsBackend *backend,
SoupMessage *msg)
{
GVfsBackendHttp *op_backend = G_VFS_BACKEND_HTTP (backend);
return soup_session_send_message (op_backend->session, msg);
}
void
http_backend_queue_message (GVfsBackend *backend,
SoupMessage *msg,
SoupSessionCallback callback,
gpointer user_data)
{
GVfsBackendHttp *op_backend = G_VFS_BACKEND_HTTP (backend);
soup_session_queue_message (op_backend->session, msg,
callback, user_data);
}
/* ************************************************************************* */ /* ************************************************************************* */
/* virtual functions overrides */ /* virtual functions overrides */
@ -235,8 +282,8 @@ try_mount (GVfsBackend *backend,
{ {
GVfsBackendHttp *op_backend; GVfsBackendHttp *op_backend;
const char *uri_str; const char *uri_str;
char *path; const char *path;
SoupURI *uri; GUri *uri;
GMountSpec *real_mount_spec; GMountSpec *real_mount_spec;
op_backend = G_VFS_BACKEND_HTTP (backend); op_backend = G_VFS_BACKEND_HTTP (backend);
@ -245,7 +292,7 @@ try_mount (GVfsBackend *backend,
uri_str = g_mount_spec_get (mount_spec, "uri"); uri_str = g_mount_spec_get (mount_spec, "uri");
if (uri_str) if (uri_str)
uri = soup_uri_new (uri_str); uri = g_uri_parse (uri_str, SOUP_HTTP_URI_FLAGS, NULL);
g_debug ("+ try_mount: %s\n", uri_str ? uri_str : "(null)"); g_debug ("+ try_mount: %s\n", uri_str ? uri_str : "(null)");
@ -260,12 +307,11 @@ try_mount (GVfsBackend *backend,
real_mount_spec = g_mount_spec_new ("http"); real_mount_spec = g_mount_spec_new ("http");
g_mount_spec_set (real_mount_spec, "uri", uri_str); g_mount_spec_set (real_mount_spec, "uri", uri_str);
if (uri->path != NULL) path = g_uri_unescape_string (g_uri_get_path (uri), "/");
if (path[0])
{ {
path = g_uri_unescape_string (uri->path, "/");
g_free (real_mount_spec->mount_prefix); g_free (real_mount_spec->mount_prefix);
real_mount_spec->mount_prefix = g_mount_spec_canonicalize_path (path); real_mount_spec->mount_prefix = g_mount_spec_canonicalize_path (path);
g_free (path);
} }
g_vfs_backend_set_mount_spec (backend, real_mount_spec); g_vfs_backend_set_mount_spec (backend, real_mount_spec);
@ -309,7 +355,7 @@ open_for_read_ready (GObject *source_object,
} }
msg = g_vfs_http_input_stream_get_message (stream); msg = g_vfs_http_input_stream_get_message (stream);
if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) if (!SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (msg)))
{ {
http_job_failed (G_VFS_JOB (job), msg); http_job_failed (G_VFS_JOB (job), msg);
g_object_unref (msg); g_object_unref (msg);
@ -330,7 +376,7 @@ try_open_for_read (GVfsBackend *backend,
GVfsJobOpenForRead *job, GVfsJobOpenForRead *job,
const char *filename) const char *filename)
{ {
SoupURI *uri; GUri *uri;
uri = http_backend_get_mount_base (backend); uri = http_backend_get_mount_base (backend);
http_backend_open_for_read (backend, G_VFS_JOB (job), uri); http_backend_open_for_read (backend, G_VFS_JOB (job), uri);
@ -341,7 +387,7 @@ try_open_for_read (GVfsBackend *backend,
void void
http_backend_open_for_read (GVfsBackend *backend, http_backend_open_for_read (GVfsBackend *backend,
GVfsJob *job, GVfsJob *job,
SoupURI *uri) GUri *uri)
{ {
GVfsBackendHttp *op_backend; GVfsBackendHttp *op_backend;
GInputStream *stream; GInputStream *stream;
@ -507,7 +553,7 @@ file_info_from_message (SoupMessage *msg,
/* prefer the filename from the Content-Disposition (rfc2183) header /* prefer the filename from the Content-Disposition (rfc2183) header
if one if present. See bug 551298. */ if one if present. See bug 551298. */
if (soup_message_headers_get_content_disposition (msg->response_headers, if (soup_message_headers_get_content_disposition (soup_message_get_response_headers (msg),
NULL, &params)) NULL, &params))
{ {
const char *name = g_hash_table_lookup (params, "filename"); const char *name = g_hash_table_lookup (params, "filename");
@ -520,10 +566,10 @@ file_info_from_message (SoupMessage *msg,
if (basename == NULL) if (basename == NULL)
{ {
const SoupURI *uri; GUri *uri;
uri = soup_message_get_uri (msg); uri = soup_message_get_uri (msg);
basename = http_uri_get_basename (uri->path); basename = http_uri_get_basename (g_uri_get_path (uri));
} }
g_debug ("basename:%s\n", basename); g_debug ("basename:%s\n", basename);
@ -540,12 +586,12 @@ file_info_from_message (SoupMessage *msg,
g_free (basename); g_free (basename);
g_free (ed_name); g_free (ed_name);
if (soup_message_headers_get_encoding (msg->response_headers) == SOUP_ENCODING_CONTENT_LENGTH) if (soup_message_headers_get_encoding (soup_message_get_response_headers (msg)) == SOUP_ENCODING_CONTENT_LENGTH)
{ {
goffset start, end, length; goffset start, end, length;
gboolean ret; gboolean ret;
ret = soup_message_headers_get_content_range (msg->response_headers, ret = soup_message_headers_get_content_range (soup_message_get_response_headers (msg),
&start, &end, &length); &start, &end, &length);
if (ret && length != -1) if (ret && length != -1)
{ {
@ -553,14 +599,14 @@ file_info_from_message (SoupMessage *msg,
} }
else if (!ret) else if (!ret)
{ {
length = soup_message_headers_get_content_length (msg->response_headers); length = soup_message_headers_get_content_length (soup_message_get_response_headers (msg));
g_file_info_set_size (info, length); g_file_info_set_size (info, length);
} }
} }
g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR); g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR);
text = soup_message_headers_get_content_type (msg->response_headers, NULL); text = soup_message_headers_get_content_type (soup_message_get_response_headers (msg), NULL);
if (text) if (text)
{ {
GIcon *icon; GIcon *icon;
@ -578,23 +624,25 @@ file_info_from_message (SoupMessage *msg,
} }
text = soup_message_headers_get_one (msg->response_headers, text = soup_message_headers_get_one (soup_message_get_response_headers (msg),
"Last-Modified"); "Last-Modified");
if (text) if (text)
{ {
SoupDate *sd; GDateTime *gd;
sd = soup_date_new_from_string(text); gd = soup_date_time_new_from_http_string (text);
if (sd) if (gd)
{ {
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED, soup_date_to_time_t (sd)); g_file_info_set_attribute_uint64 (info,
G_FILE_ATTRIBUTE_TIME_MODIFIED,
g_date_time_to_unix (gd));
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, 0); g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, 0);
soup_date_free (sd); g_date_time_unref (gd);
} }
} }
text = soup_message_headers_get_one (msg->response_headers, text = soup_message_headers_get_one (soup_message_get_response_headers (msg),
"ETag"); "ETag");
if (text) if (text)
{ {
@ -605,19 +653,25 @@ file_info_from_message (SoupMessage *msg,
} }
static void static void
query_info_ready (SoupSession *session, query_info_ready (GObject *object,
SoupMessage *msg, GAsyncResult *result,
gpointer user_data) gpointer user_data)
{ {
GFileAttributeMatcher *matcher; GVfsJobQueryInfo *job = G_VFS_JOB_QUERY_INFO (user_data);
GVfsJobQueryInfo *job; GFileAttributeMatcher *matcher = job->attribute_matcher;
GFileInfo *info; GFileInfo *info = job->file_info;
GInputStream *res;
GError *error = NULL;
SoupMessage *msg = G_VFS_JOB (job)->backend_data;
job = G_VFS_JOB_QUERY_INFO (user_data); res = soup_session_send_finish (SOUP_SESSION (object), result, &error);
info = job->file_info; if (!res)
matcher = job->attribute_matcher; {
g_vfs_job_failed_from_error (G_VFS_JOB (job), error);
return;
}
if (! SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) if (!SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (msg)))
{ {
http_job_failed (G_VFS_JOB (job), msg); http_job_failed (G_VFS_JOB (job), msg);
return; return;
@ -625,10 +679,11 @@ query_info_ready (SoupSession *session,
file_info_from_message (msg, info, matcher); file_info_from_message (msg, info, matcher);
g_object_unref (res);
g_vfs_job_succeeded (G_VFS_JOB (job)); g_vfs_job_succeeded (G_VFS_JOB (job));
} }
static gboolean static gboolean
try_query_info (GVfsBackend *backend, try_query_info (GVfsBackend *backend,
GVfsJobQueryInfo *job, GVfsJobQueryInfo *job,
@ -637,8 +692,9 @@ try_query_info (GVfsBackend *backend,
GFileInfo *info, GFileInfo *info,
GFileAttributeMatcher *attribute_matcher) GFileAttributeMatcher *attribute_matcher)
{ {
GVfsBackendHttp *op_backend = G_VFS_BACKEND_HTTP (backend);
SoupMessage *msg; SoupMessage *msg;
SoupURI *uri; GUri *uri;
if (g_file_attribute_matcher_matches_only (attribute_matcher, if (g_file_attribute_matcher_matches_only (attribute_matcher,
G_FILE_ATTRIBUTE_THUMBNAIL_PATH)) G_FILE_ATTRIBUTE_THUMBNAIL_PATH))
@ -650,7 +706,10 @@ try_query_info (GVfsBackend *backend,
uri = http_backend_get_mount_base (backend); uri = http_backend_get_mount_base (backend);
msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, uri); msg = soup_message_new_from_uri (SOUP_METHOD_HEAD, uri);
http_backend_queue_message (backend, msg, query_info_ready, job); g_vfs_job_set_backend_data (G_VFS_JOB (job), msg, NULL);
soup_session_send_async (op_backend->session, msg, G_PRIORITY_DEFAULT,
NULL, query_info_ready, job);
return TRUE; return TRUE;
} }
@ -682,19 +741,14 @@ try_query_fs_info (GVfsBackend *backend,
{ {
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "http"); g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "http");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
g_vfs_job_succeeded (G_VFS_JOB (job)); g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE; return TRUE;
} }
#define DEBUG_MAX_BODY_SIZE (100 * 1024 * 1024)
static void static void
g_vfs_backend_http_class_init (GVfsBackendHttpClass *klass) g_vfs_backend_http_class_init (GVfsBackendHttpClass *klass)
{ {
const char *debug;
SoupSessionFeature *cookie_jar;
GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GVfsBackendClass *backend_class; GVfsBackendClass *backend_class;
@ -710,43 +764,4 @@ g_vfs_backend_http_class_init (GVfsBackendHttpClass *klass)
backend_class->try_query_info = try_query_info; backend_class->try_query_info = try_query_info;
backend_class->try_query_info_on_read = try_query_info_on_read; backend_class->try_query_info_on_read = try_query_info_on_read;
backend_class->try_query_fs_info = try_query_fs_info; backend_class->try_query_fs_info = try_query_fs_info;
/* Initialize the SoupSession, common to all backend instances */
the_session = soup_session_new_with_options ("user-agent",
"gvfs/" VERSION,
NULL);
g_object_set (the_session, "ssl-strict", FALSE, NULL);
/* Cookie handling - stored temporarlly in memory, mostly useful for
* authentication in WebDAV. */
cookie_jar = g_object_new (SOUP_TYPE_COOKIE_JAR, NULL);
soup_session_add_feature (the_session, cookie_jar);
g_object_unref (cookie_jar);
/* Send Accept-Language header (see bug 166795) */
g_object_set (the_session, "accept-language-auto", TRUE, NULL);
/* Prevent connection timeouts during long operations like COPY. */
g_object_set (the_session, "timeout", 0, NULL);
/* Logging */
debug = g_getenv ("GVFS_HTTP_DEBUG");
if (debug)
{
SoupLogger *logger;
SoupLoggerLogLevel level;
if (g_ascii_strcasecmp (debug, "all") == 0 ||
g_ascii_strcasecmp (debug, "body") == 0)
level = SOUP_LOGGER_LOG_BODY;
else if (g_ascii_strcasecmp (debug, "header") == 0)
level = SOUP_LOGGER_LOG_HEADERS;
else
level = SOUP_LOGGER_LOG_MINIMAL;
logger = soup_logger_new (level, DEBUG_MAX_BODY_SIZE);
soup_session_add_feature (the_session, SOUP_SESSION_FEATURE (logger));
g_object_unref (logger);
}
} }

View File

@ -1,6 +1,7 @@
/* GIO - GLib Input, Output and Streaming Library /* GIO - GLib Input, Output and Streaming Library
* *
* Copyright (C) 2008 Red Hat, Inc. * Copyright (C) 2008 Red Hat, Inc.
* Copyright (C) 2021 Igalia S.L.
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -49,7 +50,7 @@ struct _GVfsBackendHttp
{ {
GVfsBackend parent_instance; GVfsBackend parent_instance;
SoupURI *mount_base; GUri *mount_base;
SoupSession *session; SoupSession *session;
}; };
@ -61,19 +62,14 @@ char * http_path_get_basename (const char *path_str);
int http_error_code_from_status (guint status); int http_error_code_from_status (guint status);
SoupURI * http_backend_get_mount_base (GVfsBackend *backend); SoupSession * http_try_init_session (gint max_conns,
gint max_conns_per_host);
guint http_backend_send_message (GVfsBackend *backend, GUri * http_backend_get_mount_base (GVfsBackend *backend);
SoupMessage *msg);
void http_backend_queue_message (GVfsBackend *backend,
SoupMessage *msg,
SoupSessionCallback callback,
gpointer user_data);
void http_backend_open_for_read (GVfsBackend *backend, void http_backend_open_for_read (GVfsBackend *backend,
GVfsJob *job, GVfsJob *job,
SoupURI *uri); GUri *uri);
void http_job_failed (GVfsJob *job, void http_job_failed (GVfsJob *job,
SoupMessage *msg); SoupMessage *msg);

View File

@ -390,6 +390,7 @@ do_query_fs_info (GVfsBackend *backend,
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "localtest"); g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "localtest");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_LOCAL);
file = get_g_file_from_local (filename, G_VFS_JOB (job)); file = get_g_file_from_local (filename, G_VFS_JOB (job));

View File

@ -132,6 +132,18 @@ handle_event (EventData *data, GVfsBackendMtp *backend);
* Storage name helper * Storage name helper
************************************************/ ************************************************/
/**
* create_storage_name:
*
* Returns a unique, printable storage name for a LIBMTP_devicestorage_t
* based on its StorageDescription, appending the storage ID if necessary
* to make it unique.
*
* The caller takes ownership of the returned string.
* This function never returns NULL strings.
*
* The passed-in `storage->StorageDescription` may be NULL.
*/
static char *create_storage_name (const LIBMTP_devicestorage_t *storage) static char *create_storage_name (const LIBMTP_devicestorage_t *storage)
{ {
/* The optional post-fixing of storage's name with ID requires us to /* The optional post-fixing of storage's name with ID requires us to
@ -139,9 +151,15 @@ static char *create_storage_name (const LIBMTP_devicestorage_t *storage)
or not. Since this function is called in several places, it is or not. Since this function is called in several places, it is
safest to perform this check here, each time that storage name needs safest to perform this check here, each time that storage name needs
to be created. */ to be created. */
/* TODO: The returned name is not unique if suffix-adding happens
to introduce a collision with another storage's unsuffixed
description; unlikely but possible. */
gboolean is_unique = TRUE; gboolean is_unique = TRUE;
const LIBMTP_devicestorage_t *tmp_storage; const LIBMTP_devicestorage_t *tmp_storage;
/* `storage->StorageDescription` may be NULL, so we ensure to only use
functions that can handle this, like `g_strcmp0()`. */
/* Forward search for duplicates */ /* Forward search for duplicates */
for (tmp_storage = storage->next; tmp_storage != 0; tmp_storage = tmp_storage->next) { for (tmp_storage = storage->next; tmp_storage != 0; tmp_storage = tmp_storage->next) {
if (!g_strcmp0 (storage->StorageDescription, tmp_storage->StorageDescription)) { if (!g_strcmp0 (storage->StorageDescription, tmp_storage->StorageDescription)) {
@ -164,7 +182,17 @@ static char *create_storage_name (const LIBMTP_devicestorage_t *storage)
/* If description is unique, we can use it as storage name; otherwise, /* If description is unique, we can use it as storage name; otherwise,
we add storage ID to it */ we add storage ID to it */
if (is_unique) { if (is_unique) {
return g_strdup (storage->StorageDescription); /* Never return a NULL string (`g_strdup` returns NULL on NULL).
Use the storage ID on empty strings to avoid duplicate entries
for devices with multiple storages without description. */
if (storage->StorageDescription && strlen (storage->StorageDescription) > 0) {
return g_strdup (storage->StorageDescription);
} else {
/* Translators: This is shown as the name for MTP devices
* without StorageDescription.
* The %X is the formatted storage ID. */
return g_strdup_printf (_("Storage (%X)"), storage->id);
}
} else { } else {
return g_strdup_printf ("%s (%X)", storage->StorageDescription, storage->id); return g_strdup_printf ("%s (%X)", storage->StorageDescription, storage->id);
} }
@ -1217,6 +1245,7 @@ get_storage_info (LIBMTP_devicestorage_t *storage, GFileInfo *info) {
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, storage->FreeSpaceInBytes); g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, storage->FreeSpaceInBytes);
g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, storage->MaxCapacity); g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE, storage->MaxCapacity);
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "mtpfs"); g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "mtpfs");
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_NEVER);
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, FALSE);
g_debug ("(II) get_storage_info done.\n"); g_debug ("(II) get_storage_info done.\n");
@ -1711,6 +1740,13 @@ do_pull (GVfsBackend *backend,
GFileInfo *info = NULL; GFileInfo *info = NULL;
guint64 mtime; guint64 mtime;
if (remove_source && (flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE)) {
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
goto exit;
}
CacheEntry *entry = get_cache_entry (G_VFS_BACKEND_MTP (backend), source); CacheEntry *entry = get_cache_entry (G_VFS_BACKEND_MTP (backend), source);
if (entry == NULL) { if (entry == NULL) {
g_vfs_job_failed_literal (G_VFS_JOB (job), g_vfs_job_failed_literal (G_VFS_JOB (job),
@ -2005,6 +2041,13 @@ do_push (GVfsBackend *backend,
gchar **elements = g_strsplit_set (destination, "/", -1); gchar **elements = g_strsplit_set (destination, "/", -1);
unsigned int ne = g_strv_length (elements); unsigned int ne = g_strv_length (elements);
if (remove_source && (flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE)) {
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
goto exit;
}
if (ne < 3) { if (ne < 3) {
g_vfs_job_failed_literal (G_VFS_JOB (job), g_vfs_job_failed_literal (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,

View File

@ -868,6 +868,7 @@ try_query_fs_info (GVfsBackend *backend,
{ {
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "network"); g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "network");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_NEVER);
g_vfs_job_succeeded (G_VFS_JOB (job)); g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE; return TRUE;
} }

View File

@ -1607,6 +1607,9 @@ try_query_fs_info (GVfsBackend *backend,
G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "nfs"); G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "nfs");
g_file_info_set_attribute_boolean (info, g_file_info_set_attribute_boolean (info,
G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE); G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info,
G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW,
G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
if (g_file_attribute_matcher_matches (matcher, if (g_file_attribute_matcher_matches (matcher,
G_FILE_ATTRIBUTE_FILESYSTEM_SIZE) || G_FILE_ATTRIBUTE_FILESYSTEM_SIZE) ||

View File

@ -30,7 +30,7 @@ typedef struct {
char *uri; char *uri;
char *display_name; char *display_name;
GFile *file; GFile *file;
time_t modified; GDateTime *modified;
} RecentItem; } RecentItem;
struct OPAQUE_TYPE__GVfsBackendRecent struct OPAQUE_TYPE__GVfsBackendRecent
@ -326,7 +326,8 @@ recent_backend_add_info (RecentItem *item,
TRUE); TRUE);
/* G_FILE_ATTRIBUTE_RECENT_MODIFIED */ /* G_FILE_ATTRIBUTE_RECENT_MODIFIED */
g_file_info_set_attribute_int64 (info, "recent::modified", item->modified); g_file_info_set_attribute_int64 (info, "recent::modified",
g_date_time_to_unix (item->modified));
} }
static gboolean static gboolean
@ -375,6 +376,7 @@ recent_item_free (RecentItem *item)
g_free (item->display_name); g_free (item->display_name);
g_free (item->guid); g_free (item->guid);
g_clear_object (&item->file); g_clear_object (&item->file);
g_date_time_unref (item->modified);
g_free (item); g_free (item);
} }
@ -382,7 +384,7 @@ static gboolean
recent_item_update (RecentItem *item, recent_item_update (RecentItem *item,
const gchar *uri, const gchar *uri,
const gchar *display_name, const gchar *display_name,
time_t modified) GDateTime *modified)
{ {
gboolean changed = FALSE; gboolean changed = FALSE;
@ -403,10 +405,11 @@ recent_item_update (RecentItem *item,
item->display_name = g_strdup (display_name); item->display_name = g_strdup (display_name);
} }
if (item->modified != modified) if (!g_date_time_equal (item->modified, modified))
{ {
changed = TRUE; changed = TRUE;
item->modified = modified; g_date_time_unref (item->modified);
item->modified = g_date_time_ref (modified);
} }
return changed; return changed;
@ -415,11 +418,12 @@ recent_item_update (RecentItem *item,
static RecentItem * static RecentItem *
recent_item_new (const gchar *uri, recent_item_new (const gchar *uri,
const gchar *display_name, const gchar *display_name,
time_t modified) GDateTime *modified)
{ {
RecentItem *item; RecentItem *item;
item = g_new0 (RecentItem, 1); item = g_new0 (RecentItem, 1);
item->guid = g_dbus_generate_guid (); item->guid = g_dbus_generate_guid ();
item->modified = g_date_time_ref (modified);
recent_item_update (item, uri, display_name, modified); recent_item_update (item, uri, display_name, modified);
@ -508,12 +512,12 @@ reload_recent_items (GVfsBackendRecent *backend)
const char *uri = uris[i]; const char *uri = uris[i];
const char *guid; const char *guid;
char *display_name; char *display_name;
time_t modified; GDateTime *modified;
if (should_include (backend->bookmarks, uri)) if (should_include (backend->bookmarks, uri))
{ {
display_name = get_display_name (backend->bookmarks, uri); display_name = get_display_name (backend->bookmarks, uri);
modified = g_bookmark_file_get_modified (backend->bookmarks, uri, NULL); modified = g_bookmark_file_get_modified_date_time (backend->bookmarks, uri, NULL);
guid = g_hash_table_lookup (backend->uri_map, uri); guid = g_hash_table_lookup (backend->uri_map, uri);
if (guid) if (guid)
{ {

View File

@ -465,7 +465,7 @@ setup_ssh_environment (void)
} }
static char ** static char **
setup_ssh_commandline (GVfsBackend *backend) setup_ssh_commandline (GVfsBackend *backend, const gchar *control_path)
{ {
GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend); GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
guint last_arg; guint last_arg;
@ -488,7 +488,8 @@ setup_ssh_commandline (GVfsBackend *backend)
#ifndef USE_PTY #ifndef USE_PTY
args[last_arg++] = g_strdup ("-oBatchMode yes"); args[last_arg++] = g_strdup ("-oBatchMode yes");
#endif #endif
args[last_arg++] = g_strdup ("-oControlMaster auto");
args[last_arg++] = g_strdup_printf ("-oControlPath=%s/%%C", control_path);
} }
else if (op_backend->client_vendor == SFTP_VENDOR_SSH) else if (op_backend->client_vendor == SFTP_VENDOR_SSH)
args[last_arg++] = g_strdup ("-x"); args[last_arg++] = g_strdup ("-x");
@ -1105,10 +1106,10 @@ handle_login (GVfsBackend *backend,
if (g_str_has_suffix (buffer, "password: ") || if (g_str_has_suffix (buffer, "password: ") ||
g_str_has_suffix (buffer, "Password: ") || g_str_has_suffix (buffer, "Password: ") ||
g_str_has_suffix (buffer, "Password:") || g_str_has_suffix (buffer, "Password:") ||
g_str_has_prefix (buffer, "Password for ") || strstr (buffer, "Password for ") ||
g_str_has_prefix (buffer, "Enter Kerberos password") || strstr (buffer, "Enter Kerberos password") ||
g_str_has_prefix (buffer, "Enter passphrase for key") || strstr (buffer, "Enter passphrase for key") ||
g_str_has_prefix (buffer, "Enter PASSCODE")) strstr (buffer, "Enter PASSCODE"))
{ {
gboolean aborted = FALSE; gboolean aborted = FALSE;
gsize bytes_written; gsize bytes_written;
@ -1160,17 +1161,17 @@ handle_login (GVfsBackend *backend,
if (op_backend->user_specified) if (op_backend->user_specified)
if (strcmp (authtype, "publickey") == 0) if (strcmp (authtype, "publickey") == 0)
/* Translators: the first %s is the username, the second the host name */ /* Translators: the first %s is the username, the second the host name */
prompt = g_strdup_printf (_("Enter passphrase for secure key for %s on %s"), op_backend->user, op_backend->host); prompt = g_strdup_printf (_("Authentication Required\nEnter passphrase for secure key for %s on %s”:"), op_backend->user, op_backend->host);
else else
/* Translators: the first %s is the username, the second the host name */ /* Translators: the first %s is the username, the second the host name */
prompt = g_strdup_printf (_("Enter password for %s on %s"), op_backend->user, hostname ? hostname : op_backend->host); prompt = g_strdup_printf (_("Authentication Required\nEnter password for %s on %s”:"), op_backend->user, hostname ? hostname : op_backend->host);
else else
if (strcmp (authtype, "publickey") == 0) if (strcmp (authtype, "publickey") == 0)
/* Translators: %s is the hostname */ /* Translators: %s is the hostname */
prompt = g_strdup_printf (_("Enter passphrase for secure key for %s"), op_backend->host); prompt = g_strdup_printf (_("Authentication Required\nEnter passphrase for secure key for %s”:"), op_backend->host);
else else
/* Translators: %s is the hostname */ /* Translators: %s is the hostname */
prompt = g_strdup_printf (_("Enter password for %s"), hostname ? hostname : op_backend->host); prompt = g_strdup_printf (_("Authentication Required\nEnter user and password for %s”:"), hostname ? hostname : op_backend->host);
if (!g_mount_source_ask_password (mount_source, if (!g_mount_source_ask_password (mount_source,
prompt, prompt,
@ -1264,6 +1265,55 @@ handle_login (GVfsBackend *backend,
break; break;
} }
} }
else if (strstr (buffer, "Verification code") ||
strstr (buffer, "One-time password"))
{
gchar *verification_code = NULL;
gboolean aborted = FALSE;
g_debug ("handle_login #%d - asking for verification code...\n", i);
if (op_backend->user_specified)
/* Translators: the first %s is the username, the second the host name */
prompt = g_strdup_printf (_("Enter verification code for %s on %s"),
op_backend->user, op_backend->host);
else
/* Translators: %s is the hostname */
prompt = g_strdup_printf (_("Enter verification code for %s"),
op_backend->host);
if (!g_mount_source_ask_password (mount_source, prompt,
op_backend->user, NULL,
G_ASK_PASSWORD_NEED_PASSWORD,
&aborted, &verification_code,
NULL, NULL, NULL, NULL) ||
aborted)
{
g_set_error_literal (error, G_IO_ERROR, aborted ?
G_IO_ERROR_FAILED_HANDLED :
G_IO_ERROR_PERMISSION_DENIED,
_("Password dialog cancelled"));
ret_val = FALSE;
break;
}
g_free (prompt);
if (!g_output_stream_write_all (reply_stream, verification_code,
strlen (verification_code), NULL,
NULL, NULL) ||
!g_output_stream_write_all (reply_stream, "\n", 1, NULL, NULL,
NULL))
{
g_free (verification_code);
g_set_error_literal (error,
G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
_("Cant send password"));
ret_val = FALSE;
break;
}
g_free (verification_code);
}
else if (g_str_has_prefix (buffer, "The authenticity of host '") || else if (g_str_has_prefix (buffer, "The authenticity of host '") ||
strstr (buffer, "Key fingerprint:") != NULL) strstr (buffer, "Key fingerprint:") != NULL)
{ {
@ -1275,8 +1325,10 @@ handle_login (GVfsBackend *backend,
get_hostname_and_fingerprint_from_line (buffer, &hostname, &fingerprint); get_hostname_and_fingerprint_from_line (buffer, &hostname, &fingerprint);
message = g_strdup_printf (_("Cant verify the identity of “%s”.\n" /* Translators: the first %s is the hostname, the second the key fingerprint */
"This happens when you log in to a computer the first time.\n\n" message = g_strdup_printf (_("Identity Verification Failed\n"
"Verifying the identity of “%s” failed, this happens when "
"you log in to a computer the first time.\n\n"
"The identity sent by the remote computer is “%s”. " "The identity sent by the remote computer is “%s”. "
"If you want to be absolutely sure it is safe to continue, " "If you want to be absolutely sure it is safe to continue, "
"contact the system administrator."), "contact the system administrator."),
@ -1302,7 +1354,9 @@ handle_login (GVfsBackend *backend,
get_hostname_and_ip_address (buffer, &hostname, &ip_address); get_hostname_and_ip_address (buffer, &hostname, &ip_address);
message = g_strdup_printf (_("The host key for “%s” differs from the key for the IP address “%s”\n" /* Translators: the first %s is the hostname, the second is an ip address */
message = g_strdup_printf (_("Identity Verification Failed\n"
"The host key for “%s” differs from the key for the IP address “%s”\n"
"If you want to be absolutely sure it is safe to continue, " "If you want to be absolutely sure it is safe to continue, "
"contact the system administrator."), "contact the system administrator."),
hostname ? hostname : op_backend->host, hostname ? hostname : op_backend->host,
@ -1841,8 +1895,13 @@ setup_connection (GVfsBackend *backend,
gboolean res; gboolean res;
char *extension_name, *extension_data; char *extension_name, *extension_data;
int i; int i;
gchar *control_path = NULL;
args = setup_ssh_commandline (backend); control_path = g_build_filename (g_get_user_runtime_dir (), "gvfsd-sftp", NULL);
g_mkdir (control_path, 0700);
args = setup_ssh_commandline (backend, control_path);
g_free (control_path);
if (!spawn_ssh (backend, if (!spawn_ssh (backend,
args, &pid, args, &pid,
@ -4734,6 +4793,9 @@ try_query_fs_info (GVfsBackend *backend,
G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "sftp"); G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "sftp");
g_file_info_set_attribute_boolean (info, g_file_info_set_attribute_boolean (info,
G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE); G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info,
G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW,
G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
if (has_extension (op_backend, SFTP_EXT_OPENSSH_STATVFS) && if (has_extension (op_backend, SFTP_EXT_OPENSSH_STATVFS) &&
(g_file_attribute_matcher_matches (matcher, (g_file_attribute_matcher_matches (matcher,
@ -5491,6 +5553,8 @@ typedef struct {
/* fstat information */ /* fstat information */
goffset size; goffset size;
guint32 permissions; guint32 permissions;
guint64 mtime;
guint64 atime;
/* state */ /* state */
goffset offset; goffset offset;
@ -5640,8 +5704,15 @@ push_close_deleted_file (GVfsBackendSftp *backend,
} }
static void static void
push_close_delete_or_succeed (SftpPushHandle *handle) push_close_delete_or_succeed (GVfsBackendSftp *backend,
int reply_type,
GDataInputStream *reply,
guint32 len,
GVfsJob *job,
gpointer user_data)
{ {
SftpPushHandle *handle = user_data;
if (handle->tempname) if (handle->tempname)
{ {
/* If we wrote to a temp file, do delete then rename. */ /* If we wrote to a temp file, do delete then rename. */
@ -5662,15 +5733,52 @@ push_close_delete_or_succeed (SftpPushHandle *handle)
} }
static void static void
push_close_restore_permissions (GVfsBackendSftp *backend, push_close_restore_permissions (SftpPushHandle *handle)
int reply_type,
GDataInputStream *reply,
guint32 len,
GVfsJob *job,
gpointer user_data)
{ {
/* We don't care if setting the permissions succeeded or not. */ gboolean default_perms = (handle->op_job->flags & G_FILE_COPY_TARGET_DEFAULT_PERMS);
push_close_delete_or_succeed (user_data); guint32 flags = SSH_FILEXFER_ATTR_ACMODTIME;
if (!default_perms)
flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
/* Restore the source file's permissions and timestamps. */
GDataOutputStream *command = new_command_stream (handle->backend, SSH_FXP_SETSTAT);
put_string (command, handle->tempname ? handle->tempname : handle->op_job->destination);
g_data_output_stream_put_uint32 (command, flags, NULL, NULL);
if (!default_perms)
g_data_output_stream_put_uint32 (command, handle->permissions, NULL, NULL);
g_data_output_stream_put_uint32 (command, handle->atime, NULL, NULL);
g_data_output_stream_put_uint32 (command, handle->mtime, NULL, NULL);
queue_command_stream_and_free (&handle->backend->command_connection, command,
push_close_delete_or_succeed,
handle->job, handle);
}
static void
push_close_stat_reply (GVfsBackendSftp *backend,
int reply_type,
GDataInputStream *reply,
guint32 len,
GVfsJob *job,
gpointer user_data)
{
SftpPushHandle *handle = user_data;
if (reply_type == SSH_FXP_ATTRS)
{
GFileInfo *info = g_file_info_new ();
parse_attributes (backend, info, NULL, reply, NULL);
/* Don't fail on error, but fall back to the local atime
* (assigned in push_source_fstat_cb). */
if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_ACCESS))
handle->atime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
g_object_unref (info);
}
push_close_restore_permissions (handle);
} }
static void static void
@ -5688,19 +5796,19 @@ push_close_write_reply (GVfsBackendSftp *backend,
guint32 code = read_status_code (reply); guint32 code = read_status_code (reply);
if (code == SSH_FX_OK) if (code == SSH_FX_OK)
{ {
if (handle->op_job->flags & G_FILE_COPY_TARGET_DEFAULT_PERMS) /* Atime is COPY_WHEN_MOVED, but not COPY_WITH_FILE. */
push_close_delete_or_succeed (handle); if (!handle->op_job->remove_source &&
else !(handle->op_job->flags & G_FILE_COPY_ALL_METADATA))
{ {
/* Restore the source file's permissions. */ GDataOutputStream *command = new_command_stream (backend, SSH_FXP_LSTAT);
GDataOutputStream *command = new_command_stream (backend, SSH_FXP_SETSTAT); put_string (command, handle->op_job->destination);
put_string (command, handle->tempname ? handle->tempname : handle->op_job->destination);
g_data_output_stream_put_uint32 (command, SSH_FILEXFER_ATTR_PERMISSIONS, NULL, NULL);
g_data_output_stream_put_uint32 (command, handle->permissions, NULL, NULL);
queue_command_stream_and_free (&backend->command_connection, command, queue_command_stream_and_free (&backend->command_connection, command,
push_close_restore_permissions, push_close_stat_reply,
job, handle); job, handle);
return;
} }
push_close_restore_permissions (handle);
return; return;
} }
else else
@ -6070,6 +6178,8 @@ push_source_fstat_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{ {
handle->permissions = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE) & 0777; handle->permissions = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE) & 0777;
handle->size = g_file_info_get_size (info); handle->size = g_file_info_get_size (info);
handle->mtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
handle->atime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
command = new_command_stream (handle->backend, SSH_FXP_OPEN); command = new_command_stream (handle->backend, SSH_FXP_OPEN);
put_string (command, handle->op_job->destination); put_string (command, handle->op_job->destination);
@ -6102,7 +6212,9 @@ push_source_open_cb (GObject *source, GAsyncResult *res, gpointer user_data)
g_file_input_stream_query_info_async (fin, g_file_input_stream_query_info_async (fin,
G_FILE_ATTRIBUTE_STANDARD_SIZE "," G_FILE_ATTRIBUTE_STANDARD_SIZE ","
G_FILE_ATTRIBUTE_UNIX_MODE, G_FILE_ATTRIBUTE_UNIX_MODE ","
G_FILE_ATTRIBUTE_TIME_MODIFIED ","
G_FILE_ATTRIBUTE_TIME_ACCESS,
0, NULL, 0, NULL,
push_source_fstat_cb, handle); push_source_fstat_cb, handle);
} }
@ -6166,6 +6278,14 @@ try_push (GVfsBackend *backend,
GFile *source; GFile *source;
SftpPushHandle *handle; SftpPushHandle *handle;
if (remove_source && (flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE))
{
g_vfs_job_failed (G_VFS_JOB (op_job),
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
return TRUE;
}
if (!connection_is_usable (&op_backend->data_connection)) if (!connection_is_usable (&op_backend->data_connection))
{ {
g_vfs_job_failed (G_VFS_JOB (op_job), g_vfs_job_failed (G_VFS_JOB (op_job),
@ -6211,6 +6331,8 @@ typedef struct {
/* fstat information */ /* fstat information */
goffset size; goffset size;
guint32 mode; guint32 mode;
guint64 mtime;
guint64 atime;
/* state */ /* state */
goffset offset; goffset offset;
@ -6317,12 +6439,22 @@ pull_close_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{ {
g_vfs_job_progress_callback (handle->n_written, handle->n_written, handle->job); g_vfs_job_progress_callback (handle->n_written, handle->n_written, handle->job);
if (handle->size >= 0 && !(handle->op_job->flags & G_FILE_COPY_TARGET_DEFAULT_PERMS)) if (handle->size >= 0)
{ {
GFileInfo *info = g_file_info_new (); GFileInfo *info = g_file_info_new ();
g_file_info_set_attribute_uint32 (info, if (!(handle->op_job->flags & G_FILE_COPY_TARGET_DEFAULT_PERMS))
G_FILE_ATTRIBUTE_UNIX_MODE, g_file_info_set_attribute_uint32 (info,
handle->mode); G_FILE_ATTRIBUTE_UNIX_MODE,
handle->mode);
g_file_info_set_attribute_uint64 (info,
G_FILE_ATTRIBUTE_TIME_MODIFIED,
handle->mtime);
/* Atime is COPY_WHEN_MOVED, but not COPY_WITH_FILE. */
if (handle->op_job->remove_source ||
(handle->op_job->flags & G_FILE_COPY_ALL_METADATA))
g_file_info_set_attribute_uint64 (info,
G_FILE_ATTRIBUTE_TIME_ACCESS,
handle->atime);
g_file_set_attributes_async (handle->dest, g_file_set_attributes_async (handle->dest,
info, info,
G_FILE_QUERY_INFO_NONE, G_FILE_QUERY_INFO_NONE,
@ -6573,6 +6705,10 @@ pull_fstat_reply (GVfsBackendSftp *backend,
handle->size = g_file_info_get_size (info); handle->size = g_file_info_get_size (info);
handle->mode = g_file_info_get_attribute_uint32 (info, handle->mode = g_file_info_get_attribute_uint32 (info,
G_FILE_ATTRIBUTE_UNIX_MODE); G_FILE_ATTRIBUTE_UNIX_MODE);
handle->mtime = g_file_info_get_attribute_uint64 (info,
G_FILE_ATTRIBUTE_TIME_MODIFIED);
handle->atime = g_file_info_get_attribute_uint64 (info,
G_FILE_ATTRIBUTE_TIME_ACCESS);
g_object_unref (info); g_object_unref (info);
} }
else else
@ -6699,6 +6835,14 @@ try_pull (GVfsBackend *backend,
SftpPullHandle *handle; SftpPullHandle *handle;
Command commands[2]; Command commands[2];
if (remove_source && (flags & G_FILE_COPY_NO_FALLBACK_FOR_MOVE))
{
g_vfs_job_failed (G_VFS_JOB (job),
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("Operation not supported"));
return TRUE;
}
if (!connection_is_usable (&op_backend->data_connection)) if (!connection_is_usable (&op_backend->data_connection))
{ {
g_vfs_job_failed (G_VFS_JOB (job), g_vfs_job_failed (G_VFS_JOB (job),

View File

@ -49,6 +49,7 @@
#include "gvfsjobqueryfsinfo.h" #include "gvfsjobqueryfsinfo.h"
#include "gvfsjobqueryattributes.h" #include "gvfsjobqueryattributes.h"
#include "gvfsjobenumerate.h" #include "gvfsjobenumerate.h"
#include "gvfsjobmove.h"
#include "gvfsdaemonprotocol.h" #include "gvfsdaemonprotocol.h"
#include "gvfsdaemonutils.h" #include "gvfsdaemonutils.h"
#include "gvfsutils.h" #include "gvfsutils.h"
@ -77,9 +78,7 @@ struct _GVfsBackendSmb
GMountSource *mount_source; /* Only used/set during mount */ GMountSource *mount_source; /* Only used/set during mount */
int mount_try; int mount_try;
gboolean mount_try_again;
gboolean mount_cancelled; gboolean mount_cancelled;
gboolean use_anonymous;
gboolean password_in_keyring; gboolean password_in_keyring;
GPasswordSave password_save; GPasswordSave password_save;
@ -202,25 +201,14 @@ auth_callback (SMBCCTX *context,
backend->user == NULL && backend->user == NULL &&
backend->domain == NULL) backend->domain == NULL)
{ {
/* Try again if kerberos login fails */
backend->mount_try_again = TRUE;
g_debug ("auth_callback - kerberos pass\n"); g_debug ("auth_callback - kerberos pass\n");
} }
else if (backend->mount_try == 1 && else if (backend->mount_try == 1 &&
backend->user == NULL && backend->user == NULL &&
backend->domain == NULL) backend->domain == NULL)
{ {
/* Try again if ccache login fails */
backend->mount_try_again = TRUE;
g_debug ("auth_callback - ccache pass\n"); g_debug ("auth_callback - ccache pass\n");
} }
else if (backend->use_anonymous)
{
/* Try again if anonymous login fails */
backend->use_anonymous = FALSE;
backend->mount_try_again = TRUE;
g_debug ("auth_callback - anonymous login pass\n");
}
else else
{ {
gboolean in_keyring = FALSE; gboolean in_keyring = FALSE;
@ -263,9 +251,19 @@ auth_callback (SMBCCTX *context,
g_debug ("auth_callback - asking for password...\n"); g_debug ("auth_callback - asking for password...\n");
/* translators: First %s is a share name, second is a server name */ if (backend->user)
message = g_strdup_printf (_("Password required for share %s on %s"), {
share_name, server_name); /* Translators: First %s is a share name, second is a server name */
message = g_strdup_printf (_("Authentication Required\nEnter password for share “%s” on “%s”:"),
share_name, server_name);
}
else
{
/* Translators: First %s is a share name, second is a server name */
message = g_strdup_printf (_("Authentication Required\nEnter user and password for share “%s” on “%s”:"),
share_name, server_name);
}
handled = g_mount_source_ask_password (backend->mount_source, handled = g_mount_source_ask_password (backend->mount_source,
message, message,
username_out, username_out,
@ -290,13 +288,13 @@ auth_callback (SMBCCTX *context,
} }
} }
/* Try again if this fails */ smbc_setOptionNoAutoAnonymousLogin (backend->smb_context,
backend->mount_try_again = TRUE; !anonymous);
if (anonymous) if (anonymous)
{ {
backend->use_anonymous = TRUE;
backend->password_save = FALSE; backend->password_save = FALSE;
g_debug ("auth_callback - anonymous enabled\n");
} }
else else
{ {
@ -486,7 +484,6 @@ do_mount (GVfsBackend *backend,
*/ */
do do
{ {
op_backend->mount_try_again = FALSE;
op_backend->mount_cancelled = FALSE; op_backend->mount_cancelled = FALSE;
g_debug ("do_mount - try #%d \n", op_backend->mount_try); g_debug ("do_mount - try #%d \n", op_backend->mount_try);
@ -502,7 +499,13 @@ do_mount (GVfsBackend *backend,
if (res == 0) if (res == 0)
break; break;
if (op_backend->mount_cancelled || (errsv != EACCES && errsv != EPERM)) if (errsv == EINVAL && op_backend->mount_try <= 1 && op_backend->user == NULL)
{
/* EINVAL is "expected" when kerberos/ccache is misconfigured, see:
* https://gitlab.gnome.org/GNOME/gvfs/-/issues/611
*/
}
else if (op_backend->mount_cancelled || (errsv != EACCES && errsv != EPERM))
{ {
g_debug ("do_mount - (errno != EPERM && errno != EACCES), cancelled = %d, breaking\n", op_backend->mount_cancelled); g_debug ("do_mount - (errno != EPERM && errno != EACCES), cancelled = %d, breaking\n", op_backend->mount_cancelled);
break; break;
@ -518,15 +521,9 @@ do_mount (GVfsBackend *backend,
smbc_setOptionFallbackAfterKerberos (op_backend->smb_context, 1); smbc_setOptionFallbackAfterKerberos (op_backend->smb_context, 1);
} }
/* If the AskPassword reply requested anonymous login, enable the
* anonymous fallback and try again.
*/
smbc_setOptionNoAutoAnonymousLogin (op_backend->smb_context,
!op_backend->use_anonymous);
op_backend->mount_try ++; op_backend->mount_try ++;
} }
while (op_backend->mount_try_again); while (TRUE);
g_free (uri); g_free (uri);
@ -1434,6 +1431,8 @@ set_info_from_stat (GVfsBackendSmb *backend,
if (g_file_attribute_matcher_matches (matcher, if (g_file_attribute_matcher_matches (matcher,
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE) || G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE) ||
g_file_attribute_matcher_matches (matcher,
G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE) ||
g_file_attribute_matcher_matches (matcher, g_file_attribute_matcher_matches (matcher,
G_FILE_ATTRIBUTE_STANDARD_ICON) || G_FILE_ATTRIBUTE_STANDARD_ICON) ||
g_file_attribute_matcher_matches (matcher, g_file_attribute_matcher_matches (matcher,
@ -1587,6 +1586,7 @@ do_query_fs_info (GVfsBackend *backend,
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "cifs"); g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "cifs");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_IF_ALWAYS);
if (g_file_attribute_matcher_matches (attribute_matcher, if (g_file_attribute_matcher_matches (attribute_matcher,
G_FILE_ATTRIBUTE_FILESYSTEM_SIZE) || G_FILE_ATTRIBUTE_FILESYSTEM_SIZE) ||
@ -2041,6 +2041,7 @@ do_move (GVfsBackend *backend,
smbc_stat_fn smbc_stat; smbc_stat_fn smbc_stat;
smbc_rename_fn smbc_rename; smbc_rename_fn smbc_rename;
smbc_unlink_fn smbc_unlink; smbc_unlink_fn smbc_unlink;
goffset size;
source_uri = create_smb_uri (op_backend->server, op_backend->port, op_backend->share, source); source_uri = create_smb_uri (op_backend->server, op_backend->port, op_backend->share, source);
@ -2062,8 +2063,9 @@ do_move (GVfsBackend *backend,
g_free (source_uri); g_free (source_uri);
return; return;
} }
else
source_is_dir = S_ISDIR (statbuf.st_mode); source_is_dir = S_ISDIR (statbuf.st_mode);
size = statbuf.st_size;
dest_uri = create_smb_uri (op_backend->server, op_backend->port, op_backend->share, destination); dest_uri = create_smb_uri (op_backend->server, op_backend->port, op_backend->share, destination);
@ -2158,7 +2160,10 @@ do_move (GVfsBackend *backend,
g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv); g_vfs_job_failed_from_errno (G_VFS_JOB (job), errsv);
} }
else else
g_vfs_job_succeeded (G_VFS_JOB (job)); {
g_vfs_job_progress_callback (size, size, job);
g_vfs_job_succeeded (G_VFS_JOB (job));
}
} }
static void static void

View File

@ -367,9 +367,19 @@ auth_callback (SMBCCTX *context,
g_debug ("auth_callback - asking for password...\n"); g_debug ("auth_callback - asking for password...\n");
/* translators: %s is a server name */ if (backend->user)
message = g_strdup_printf (_("Password required for %s"), {
server_name); /* Translators: %s is a server name */
message = g_strdup_printf (_("Authentication Required\nEnter password for “%s”:"),
server_name);
}
else
{
/* Translators: %s is a server name */
message = g_strdup_printf (_("Authentication Required\nEnter user and password for “%s”:"),
server_name);
}
handled = g_mount_source_ask_password (backend->mount_source, handled = g_mount_source_ask_password (backend->mount_source,
message, message,
username_out, username_out,
@ -957,8 +967,14 @@ do_mount (GVfsBackend *backend,
uri, op_backend->mount_try, dir, op_backend->mount_cancelled, uri, op_backend->mount_try, dir, op_backend->mount_cancelled,
errsv, g_strerror (errsv)); errsv, g_strerror (errsv));
if (dir == NULL && if (errsv == EINVAL && op_backend->mount_try == 0 && op_backend->user == NULL)
(op_backend->mount_cancelled || (errsv != EPERM && errsv != EACCES))) {
/* EINVAL is "expected" when kerberos is misconfigured, see:
* https://gitlab.gnome.org/GNOME/gvfs/-/issues/611
*/
}
else if (dir == NULL &&
(op_backend->mount_cancelled || (errsv != EPERM && errsv != EACCES)))
{ {
g_debug ("do_mount - (errno != EPERM && errno != EACCES), cancelled = %d, breaking\n", op_backend->mount_cancelled); g_debug ("do_mount - (errno != EPERM && errno != EACCES), cancelled = %d, breaking\n", op_backend->mount_cancelled);
break; break;
@ -1496,6 +1512,7 @@ try_query_fs_info (GVfsBackend *backend,
{ {
g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "cifs"); g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FILESYSTEM_TYPE, "cifs");
g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE); g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_REMOTE, TRUE);
g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_FILESYSTEM_USE_PREVIEW, G_FILESYSTEM_PREVIEW_TYPE_NEVER);
g_vfs_job_succeeded (G_VFS_JOB (job)); g_vfs_job_succeeded (G_VFS_JOB (job));
return TRUE; return TRUE;
} }

View File

@ -612,8 +612,7 @@ send_reply_cb (GObject *source_object,
job = channel->priv->current_job; job = channel->priv->current_job;
channel->priv->current_job = NULL; channel->priv->current_job = NULL;
if (job) g_vfs_job_emit_finished (job);
g_vfs_job_emit_finished (job);
class = G_VFS_CHANNEL_GET_CLASS (channel); class = G_VFS_CHANNEL_GET_CLASS (channel);
@ -634,7 +633,7 @@ send_reply_cb (GObject *source_object,
} }
/* Start queued request or readahead */ /* Start queued request or readahead */
else if (!start_queued_request (channel) && else if (!start_queued_request (channel) &&
class->readahead && job) class->readahead)
{ {
/* No queued requests, maybe we want to do a readahead call */ /* No queued requests, maybe we want to do a readahead call */
channel->priv->current_job = class->readahead (channel, job); channel->priv->current_job = class->readahead (channel, job);
@ -644,7 +643,6 @@ send_reply_cb (GObject *source_object,
} }
g_object_unref (job); g_object_unref (job);
g_object_unref (channel);
} }
/* Might be called on an i/o thread */ /* Might be called on an i/o thread */
@ -668,7 +666,7 @@ g_vfs_channel_send_reply (GVfsChannel *channel,
channel->priv->reply_buffer, channel->priv->reply_buffer,
G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_SIZE, G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_SIZE,
0, NULL, 0, NULL,
send_reply_cb, g_object_ref (channel)); send_reply_cb, channel);
} }
else else
{ {
@ -677,7 +675,7 @@ g_vfs_channel_send_reply (GVfsChannel *channel,
channel->priv->output_data, channel->priv->output_data,
channel->priv->output_data_size, channel->priv->output_data_size,
0, NULL, 0, NULL,
send_reply_cb, g_object_ref (channel)); send_reply_cb, channel);
} }
} }

View File

@ -87,14 +87,6 @@ struct _GVfsDaemon
gboolean lost_main_daemon; gboolean lost_main_daemon;
}; };
typedef struct {
GVfsDaemon *daemon;
char *socket_dir;
GDBusServer *server;
GDBusConnection *conn;
} NewConnectionData;
static guint signals[LAST_SIGNAL] = { 0 }; static guint signals[LAST_SIGNAL] = { 0 };
static void g_vfs_daemon_get_property (GObject *object, static void g_vfs_daemon_get_property (GObject *object,
@ -209,7 +201,6 @@ job_handler_callback (gpointer data,
GVfsJob *job = G_VFS_JOB (data); GVfsJob *job = G_VFS_JOB (data);
g_vfs_job_run (job); g_vfs_job_run (job);
g_object_unref (job);
} }
static void static void
@ -649,30 +640,10 @@ g_vfs_daemon_queue_job (GVfsDaemon *daemon,
if (!g_vfs_job_try (job)) if (!g_vfs_job_try (job))
{ {
/* Couldn't finish / run async, queue worker thread */ /* Couldn't finish / run async, queue worker thread */
if (!g_thread_pool_push (daemon->thread_pool, g_object_ref (job), NULL)) /* TODO: Check error */ g_thread_pool_push (daemon->thread_pool, job, NULL); /* TODO: Check error */
g_object_unref (job);
} }
} }
static void
new_connection_data_free (void *memory)
{
NewConnectionData *data = memory;
gchar *socket;
/* Remove the socket and dir after connected */
if (data->socket_dir)
{
socket = g_strdup_printf ("%s/socket", data->socket_dir);
g_unlink (socket);
g_free (socket);
rmdir (data->socket_dir);
g_free (data->socket_dir);
}
g_free (data);
}
static void static void
peer_unregister_skeleton (const gchar *obj_path, peer_unregister_skeleton (const gchar *obj_path,
RegisteredPath *reg_path, RegisteredPath *reg_path,
@ -723,21 +694,19 @@ peer_connection_closed (GDBusConnection *connection,
daemon_skeleton = g_object_get_data (G_OBJECT (connection), "daemon_skeleton"); daemon_skeleton = g_object_get_data (G_OBJECT (connection), "daemon_skeleton");
/* daemon_skeleton should be always valid in this case */ /* daemon_skeleton should be always valid in this case */
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (daemon_skeleton)); g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (daemon_skeleton));
g_hash_table_remove (daemon->client_connections, connection);
/* Unexport the registered interface skeletons */ /* Unexport the registered interface skeletons */
g_hash_table_foreach (daemon->registered_paths, (GHFunc) peer_unregister_skeleton, connection); g_hash_table_foreach (daemon->registered_paths, (GHFunc) peer_unregister_skeleton, connection);
/* The peer-to-peer connection was disconnected */ /* The peer-to-peer connection was disconnected */
g_signal_handlers_disconnect_by_data (connection, user_data); g_signal_handlers_disconnect_by_data (connection, user_data);
g_object_unref (connection);
g_hash_table_remove (daemon->client_connections, connection);
} }
static void static void
daemon_peer_connection_setup (GVfsDaemon *daemon, daemon_peer_connection_setup (GVfsDaemon *daemon,
GDBusConnection *dbus_conn, GDBusConnection *dbus_conn)
NewConnectionData *data)
{ {
GVfsDBusDaemon *daemon_skeleton; GVfsDBusDaemon *daemon_skeleton;
GError *error; GError *error;
@ -754,133 +723,34 @@ daemon_peer_connection_setup (GVfsDaemon *daemon,
g_warning ("Failed to accept client: %s, %s (%s, %d)", "object registration failed", g_warning ("Failed to accept client: %s, %s (%s, %d)", "object registration failed",
error->message, g_quark_to_string (error->domain), error->code); error->message, g_quark_to_string (error->domain), error->code);
g_error_free (error); g_error_free (error);
g_object_unref (data->conn); return;
goto error_out;
} }
g_object_set_data_full (G_OBJECT (data->conn), "daemon_skeleton", daemon_skeleton, (GDestroyNotify) g_object_unref); g_object_set_data_full (G_OBJECT (dbus_conn), "daemon_skeleton",
daemon_skeleton, (GDestroyNotify) g_object_unref);
/* Export registered interface skeletons on this new connection */ /* Export registered interface skeletons on this new connection */
g_hash_table_foreach (daemon->registered_paths, (GHFunc) peer_register_skeleton, dbus_conn); g_hash_table_foreach (daemon->registered_paths, (GHFunc) peer_register_skeleton, dbus_conn);
g_hash_table_insert (daemon->client_connections, g_object_ref (dbus_conn), NULL); g_hash_table_insert (daemon->client_connections, g_object_ref (dbus_conn), NULL);
g_signal_connect (data->conn, "closed", G_CALLBACK (peer_connection_closed), data->daemon); g_signal_connect (dbus_conn, "closed", G_CALLBACK (peer_connection_closed), daemon);
error_out:
new_connection_data_free (data);
} }
#ifdef __linux__
#define USE_ABSTRACT_SOCKETS
#endif
#ifndef USE_ABSTRACT_SOCKETS
static gboolean
test_safe_socket_dir (const char *dirname)
{
struct stat statbuf;
if (g_stat (dirname, &statbuf) != 0)
return FALSE;
#ifndef G_PLATFORM_WIN32
if (statbuf.st_uid != getuid ())
return FALSE;
if ((statbuf.st_mode & (S_IRWXG|S_IRWXO)) ||
!S_ISDIR (statbuf.st_mode))
return FALSE;
#endif
return TRUE;
}
static char *
create_socket_dir (void)
{
char *dirname;
long iteration = 0;
char *safe_dir;
gchar tmp[9];
int i;
safe_dir = NULL;
do
{
g_free (safe_dir);
gvfs_randomize_string (tmp, 8);
tmp[8] = '\0';
dirname = g_strdup_printf ("gvfs-%s-%s",
g_get_user_name (), tmp);
safe_dir = g_build_filename (g_get_tmp_dir (), dirname, NULL);
g_free (dirname);
if (g_mkdir (safe_dir, 0700) < 0)
{
switch (errno)
{
case EACCES:
g_error ("I can't write to '%s', daemon init failed",
safe_dir);
break;
case ENAMETOOLONG:
g_error ("Name '%s' too long your system is broken",
safe_dir);
break;
case ENOMEM:
#ifdef ELOOP
case ELOOP:
#endif
case ENOSPC:
case ENOTDIR:
case ENOENT:
g_error ("Resource problem creating '%s'", safe_dir);
break;
default: /* carry on going */
break;
}
}
/* Possible race - so we re-scan. */
if (iteration++ == 1000)
g_error ("Cannot find a safe socket path in '%s'", g_get_tmp_dir ());
}
while (!test_safe_socket_dir (safe_dir));
return safe_dir;
}
#endif
static void static void
generate_address (char **address, generate_address (gchar **address, gchar **socket_path)
char **folder)
{ {
*address = NULL; gchar tmp[16] = "socket-";
*folder = NULL; gchar *socket_dir;
#ifdef USE_ABSTRACT_SOCKETS gvfs_randomize_string (tmp + 7, 8);
{ tmp[15] = '\0';
gchar tmp[9];
gvfs_randomize_string (tmp, 8); socket_dir = gvfs_get_socket_dir ();
tmp[8] = '\0';
*address = g_strdup_printf ("unix:abstract=/dbus-vfs-daemon/socket-%s", tmp); *socket_path = g_build_filename (socket_dir, tmp, NULL);
} *address = g_strdup_printf ("unix:path=%s", *socket_path);
#else
{ g_free (socket_dir);
char *dir;
dir = create_socket_dir ();
*address = g_strdup_printf ("unix:path=%s/socket", dir);
*folder = dir;
}
#endif
} }
static gboolean static gboolean
@ -888,14 +758,9 @@ daemon_new_connection_func (GDBusServer *server,
GDBusConnection *connection, GDBusConnection *connection,
gpointer user_data) gpointer user_data)
{ {
NewConnectionData *data; GVfsDaemon *daemon = user_data;
data = user_data; daemon_peer_connection_setup (daemon, connection);
/* Take ownership */
data->conn = g_object_ref (connection);
daemon_peer_connection_setup (data->daemon, data->conn, data);
/* Kill the server, no more need for it */ /* Kill the server, no more need for it */
g_dbus_server_stop (server); g_dbus_server_stop (server);
@ -913,16 +778,11 @@ handle_get_connection (GVfsDBusDaemon *object,
GDBusServer *server; GDBusServer *server;
GError *error; GError *error;
gchar *address1; gchar *address1;
NewConnectionData *data; gchar *socket_path;
char *socket_dir;
gchar *guid; gchar *guid;
const char *pkexec_uid;
generate_address (&address1, &socket_dir);
data = g_new (NewConnectionData, 1); generate_address (&address1, &socket_path);
data->daemon = daemon;
data->socket_dir = socket_dir;
data->conn = NULL;
guid = g_dbus_generate_guid (); guid = g_dbus_generate_guid ();
error = NULL; error = NULL;
@ -943,21 +803,34 @@ handle_get_connection (GVfsDBusDaemon *object,
} }
g_dbus_server_start (server); g_dbus_server_start (server);
data->server = server;
g_signal_connect (server, "new-connection", G_CALLBACK (daemon_new_connection_func), data); /* This is needed for gvfsd-admin to ensure correct ownership. */
pkexec_uid = g_getenv ("PKEXEC_UID");
if (pkexec_uid != NULL)
{
uid_t uid;
uid = strtol (pkexec_uid, NULL, 10);
if (uid != 0)
if (chown (socket_path, uid, (gid_t)-1) < 0)
g_warning ("Failed to change socket ownership: %s", g_strerror (errno));
}
g_signal_connect (server, "new-connection", G_CALLBACK (daemon_new_connection_func), daemon);
gvfs_dbus_daemon_complete_get_connection (object, gvfs_dbus_daemon_complete_get_connection (object,
invocation, invocation,
address1, address1,
""); "");
g_free (address1); g_free (address1);
g_free (socket_path);
return TRUE; return TRUE;
error_out: error_out:
new_connection_data_free (data);
g_free (address1); g_free (address1);
g_unlink (socket_path);
g_free (socket_path);
return TRUE; return TRUE;
} }
@ -1171,8 +1044,7 @@ void
g_vfs_daemon_run_job_in_thread (GVfsDaemon *daemon, g_vfs_daemon_run_job_in_thread (GVfsDaemon *daemon,
GVfsJob *job) GVfsJob *job)
{ {
if (!g_thread_pool_push (daemon->thread_pool, g_object_ref (job), NULL)) /* TODO: Check error */ g_thread_pool_push (daemon->thread_pool, job, NULL); /* TODO: Check error */
g_object_unref (job);
} }
void void

View File

@ -332,7 +332,9 @@ gvfs_accept_certificate (GMountSource *mount_source,
certificate_str = certificate_to_string (certificate); certificate_str = certificate_to_string (certificate);
reason = certificate_flags_to_string (errors); reason = certificate_flags_to_string (errors);
message = g_strdup_printf (_("The sites identity cant be verified:"
/* Translators: The first %s is the reason why verification failed, the second a certificate */
message = g_strdup_printf (_("Identity Verification Failed\n"
"%s\n\n" "%s\n\n"
"%s\n\n" "%s\n\n"
"Are you really sure you would like to continue?"), "Are you really sure you would like to continue?"),
@ -361,3 +363,77 @@ gvfs_accept_certificate (GMountSource *mount_source,
return FALSE; return FALSE;
} }
#endif #endif
gssize
gvfs_output_stream_splice (GOutputStream *stream,
GInputStream *source,
GOutputStreamSpliceFlags flags,
goffset total_size,
GFileProgressCallback progress_callback,
gpointer progress_callback_data,
GCancellable *cancellable,
GError **error)
{
gssize n_read, n_written;
gsize bytes_copied;
char buffer[8192], *p;
gboolean res;
bytes_copied = 0;
res = TRUE;
do
{
n_read = g_input_stream_read (source, buffer, sizeof (buffer), cancellable, error);
if (n_read == -1)
{
res = FALSE;
break;
}
if (n_read == 0)
break;
p = buffer;
while (n_read > 0)
{
n_written = g_output_stream_write (stream, p, n_read, cancellable, error);
if (n_written == -1)
{
res = FALSE;
break;
}
p += n_written;
n_read -= n_written;
bytes_copied += n_written;
if (progress_callback)
progress_callback (bytes_copied, total_size, progress_callback_data);
}
if (bytes_copied > G_MAXSSIZE)
bytes_copied = G_MAXSSIZE;
}
while (res);
if (!res)
error = NULL; /* Ignore further errors */
if (flags & G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE)
{
/* Don't care about errors in source here */
g_input_stream_close (source, cancellable, NULL);
}
if (flags & G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET)
{
/* But write errors on close are bad! */
if (!g_output_stream_close (stream, cancellable, error))
res = FALSE;
}
if (res)
return bytes_copied;
return -1;
}

View File

@ -47,6 +47,15 @@ gboolean gvfs_accept_certificate (GMountSource *mount_source,
GTlsCertificate *certificate, GTlsCertificate *certificate,
GTlsCertificateFlags errors); GTlsCertificateFlags errors);
gssize gvfs_output_stream_splice (GOutputStream *stream,
GInputStream *source,
GOutputStreamSpliceFlags flags,
goffset total_size,
GFileProgressCallback progress_callback,
gpointer progress_callback_data,
GCancellable *cancellable,
GError **error);
G_END_DECLS G_END_DECLS
#endif /* __G_VFS_DAEMON_UTILS_H__ */ #endif /* __G_VFS_DAEMON_UTILS_H__ */

View File

@ -231,6 +231,7 @@ g_vfs_ftp_task_acquire_connection (GVfsFtpTask *task)
if (g_vfs_ftp_connection_is_usable (task->conn)) if (g_vfs_ftp_connection_is_usable (task->conn))
break; break;
ftp->connections--;
g_vfs_ftp_connection_free (task->conn); g_vfs_ftp_connection_free (task->conn);
task->conn = NULL; task->conn = NULL;
} }

View File

@ -1,6 +1,7 @@
/* gvfshttpinputstream.c: seekable wrapper around SoupRequestHTTP /* gvfshttpinputstream.c: seekable wrapper around SoupRequestHTTP
* *
* Copyright (C) 2006, 2007, 2012 Red Hat, Inc. * Copyright (C) 2006, 2007, 2012 Red Hat, Inc.
* Copyright (C) 2021 Igalia S.L.
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -29,13 +30,13 @@
#include <libsoup/soup.h> #include <libsoup/soup.h>
#include "gvfshttpinputstream.h" #include "gvfshttpinputstream.h"
#include "gvfsbackendhttp.h"
static void g_vfs_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface); static void g_vfs_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface);
struct GVfsHttpInputStreamPrivate { struct GVfsHttpInputStreamPrivate {
SoupURI *uri; GUri *uri;
SoupSession *session; SoupSession *session;
SoupRequest *req;
SoupMessage *msg; SoupMessage *msg;
GInputStream *stream; GInputStream *stream;
@ -61,9 +62,8 @@ g_vfs_http_input_stream_finalize (GObject *object)
GVfsHttpInputStream *stream = G_VFS_HTTP_INPUT_STREAM (object); GVfsHttpInputStream *stream = G_VFS_HTTP_INPUT_STREAM (object);
GVfsHttpInputStreamPrivate *priv = stream->priv; GVfsHttpInputStreamPrivate *priv = stream->priv;
g_clear_pointer (&priv->uri, soup_uri_free); g_clear_pointer (&priv->uri, g_uri_unref);
g_clear_object (&priv->session); g_clear_object (&priv->session);
g_clear_object (&priv->req);
g_clear_object (&priv->msg); g_clear_object (&priv->msg);
g_clear_object (&priv->stream); g_clear_object (&priv->stream);
g_free (priv->range); g_free (priv->range);
@ -74,7 +74,7 @@ g_vfs_http_input_stream_finalize (GObject *object)
/** /**
* g_vfs_http_input_stream_new: * g_vfs_http_input_stream_new:
* @session: a #SoupSession * @session: a #SoupSession
* @uri: a #SoupURI * @uri: a #GUri
* *
* Prepares to send a GET request for @uri on @session, and returns a * Prepares to send a GET request for @uri on @session, and returns a
* #GInputStream that can be used to read the response. * #GInputStream that can be used to read the response.
@ -89,7 +89,7 @@ g_vfs_http_input_stream_finalize (GObject *object)
**/ **/
GInputStream * GInputStream *
g_vfs_http_input_stream_new (SoupSession *session, g_vfs_http_input_stream_new (SoupSession *session,
SoupURI *uri) GUri *uri)
{ {
GVfsHttpInputStream *stream; GVfsHttpInputStream *stream;
GVfsHttpInputStreamPrivate *priv; GVfsHttpInputStreamPrivate *priv;
@ -98,30 +98,27 @@ g_vfs_http_input_stream_new (SoupSession *session,
priv = stream->priv; priv = stream->priv;
priv->session = g_object_ref (session); priv->session = g_object_ref (session);
priv->uri = soup_uri_copy (uri); priv->uri = g_uri_ref (uri);
return G_INPUT_STREAM (stream); return G_INPUT_STREAM (stream);
} }
static SoupRequest * static SoupMessage *
g_vfs_http_input_stream_ensure_request (GInputStream *stream) g_vfs_http_input_stream_ensure_msg (GInputStream *stream)
{ {
GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM (stream)->priv; GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM (stream)->priv;
if (!priv->req) if (!priv->msg)
{ {
GError *error = NULL; priv->msg = soup_message_new_from_uri (SOUP_METHOD_GET, priv->uri);
priv->req = soup_session_request_uri (priv->session, priv->uri, &error);
g_assert_no_error (error);
priv->msg = soup_request_http_get_message (SOUP_REQUEST_HTTP (priv->req));
priv->offset = 0; priv->offset = 0;
} }
if (priv->range) if (priv->range)
soup_message_headers_replace (priv->msg->request_headers, "Range", priv->range); soup_message_headers_replace (soup_message_get_request_headers (priv->msg),
"Range", priv->range);
return priv->req; return priv->msg;
} }
static void static void
@ -136,7 +133,7 @@ send_callback (GObject *object,
g_input_stream_clear_pending (http_stream); g_input_stream_clear_pending (http_stream);
priv->stream = soup_request_send_finish (SOUP_REQUEST (object), result, &error); priv->stream = soup_session_send_finish (SOUP_SESSION (object), result, &error);
if (priv->stream) if (priv->stream)
g_task_return_boolean (task, TRUE); g_task_return_boolean (task, TRUE);
else else
@ -188,9 +185,9 @@ g_vfs_http_input_stream_send_async (GInputStream *stream,
return; return;
} }
g_vfs_http_input_stream_ensure_request (stream); g_vfs_http_input_stream_ensure_msg (stream);
soup_request_send_async (priv->req, cancellable, soup_session_send_async (priv->session, priv->msg, G_PRIORITY_DEFAULT,
send_callback, task); cancellable, send_callback, task);
} }
/** /**
@ -253,16 +250,16 @@ read_send_callback (GObject *object,
ReadAfterSendData *rasd = g_task_get_task_data (task); ReadAfterSendData *rasd = g_task_get_task_data (task);
GError *error = NULL; GError *error = NULL;
priv->stream = soup_request_send_finish (SOUP_REQUEST (object), result, &error); priv->stream = soup_session_send_finish (SOUP_SESSION (object), result, &error);
if (!priv->stream) if (!priv->stream)
{ {
g_task_return_error (task, error); g_task_return_error (task, error);
g_object_unref (task); g_object_unref (task);
return; return;
} }
if (!SOUP_STATUS_IS_SUCCESSFUL (priv->msg->status_code)) if (!SOUP_STATUS_IS_SUCCESSFUL (soup_message_get_status (priv->msg)))
{ {
if (priv->msg->status_code == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE) if (soup_message_get_status (priv->msg) == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE)
{ {
g_input_stream_close (priv->stream, NULL, NULL); g_input_stream_close (priv->stream, NULL, NULL);
g_task_return_int (task, 0); g_task_return_int (task, 0);
@ -271,9 +268,9 @@ read_send_callback (GObject *object,
return; return;
} }
g_task_return_new_error (task, g_task_return_new_error (task,
SOUP_HTTP_ERROR, G_IO_ERROR,
priv->msg->status_code, http_error_code_from_status (soup_message_get_status (priv->msg)),
"%s", priv->msg->reason_phrase); _("HTTP Error: %s"), soup_message_get_reason_phrase (priv->msg));
g_object_unref (task); g_object_unref (task);
return; return;
} }
@ -282,7 +279,7 @@ read_send_callback (GObject *object,
gboolean status; gboolean status;
goffset start, end; goffset start, end;
status = soup_message_headers_get_content_range (priv->msg->response_headers, status = soup_message_headers_get_content_range (soup_message_get_response_headers (priv->msg),
&start, &end, NULL); &start, &end, NULL);
if (!status || start != priv->request_offset) if (!status || start != priv->request_offset)
{ {
@ -325,9 +322,9 @@ g_vfs_http_input_stream_read_async (GInputStream *stream,
rasd->count = count; rasd->count = count;
g_task_set_task_data (task, rasd, g_free); g_task_set_task_data (task, rasd, g_free);
g_vfs_http_input_stream_ensure_request (stream); g_vfs_http_input_stream_ensure_msg (stream);
soup_request_send_async (priv->req, cancellable, soup_session_send_async (priv->session, priv->msg, G_PRIORITY_DEFAULT,
read_send_callback, task); cancellable, read_send_callback, task);
return; return;
} }
@ -419,7 +416,7 @@ g_vfs_http_input_stream_seek (GSeekable *seekable,
if (type == G_SEEK_END && priv->msg) if (type == G_SEEK_END && priv->msg)
{ {
goffset content_length = soup_message_headers_get_content_length (priv->msg->response_headers); goffset content_length = soup_message_headers_get_content_length (soup_message_get_response_headers (priv->msg));
if (content_length) if (content_length)
{ {
@ -498,7 +495,7 @@ g_vfs_http_input_stream_get_message (GInputStream *stream)
{ {
GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM (stream)->priv; GVfsHttpInputStreamPrivate *priv = G_VFS_HTTP_INPUT_STREAM (stream)->priv;
g_vfs_http_input_stream_ensure_request (stream); g_vfs_http_input_stream_ensure_msg (stream);
return g_object_ref (priv->msg); return g_object_ref (priv->msg);
} }

View File

@ -1,4 +1,5 @@
/* Copyright (C) 2006, 2007, 2012 Red Hat, Inc. /* Copyright (C) 2006, 2007, 2012 Red Hat, Inc.
* Copyright (C) 2021 Igalia S.L.
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -57,7 +58,7 @@ struct GVfsHttpInputStreamClass
GType g_vfs_http_input_stream_get_type (void) G_GNUC_CONST; GType g_vfs_http_input_stream_get_type (void) G_GNUC_CONST;
GInputStream *g_vfs_http_input_stream_new (SoupSession *session, GInputStream *g_vfs_http_input_stream_new (SoupSession *session,
SoupURI *uri); GUri *uri);
void g_vfs_http_input_stream_send_async (GInputStream *stream, void g_vfs_http_input_stream_send_async (GInputStream *stream,
int io_priority, int io_priority,

View File

@ -36,7 +36,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
GType GType
g_vfs_job_source_get_type (void) g_vfs_job_source_get_type (void)
{ {
static volatile gsize g_define_type_id__volatile = 0; static gsize g_define_type_id__volatile = 0;
if (g_once_init_enter (&g_define_type_id__volatile)) if (g_once_init_enter (&g_define_type_id__volatile))
{ {

View File

@ -96,9 +96,7 @@ on_name_acquired (GDBusConnection *connection,
argv2[0] = LIBEXEC_DIR "/gvfsd-fuse"; argv2[0] = LIBEXEC_DIR "/gvfsd-fuse";
argv2[1] = fuse_path; argv2[1] = fuse_path;
argv2[2] = "-f"; argv2[2] = "-f";
argv2[3] = "-o"; argv2[3] = NULL;
argv2[4] = "big_writes";
argv2[5] = NULL;
g_spawn_async (NULL, g_spawn_async (NULL,
argv2, argv2,
@ -145,6 +143,7 @@ main (int argc, char *argv[])
guint name_owner_id; guint name_owner_id;
GBusNameOwnerFlags flags; GBusNameOwnerFlags flags;
GOptionContext *context; GOptionContext *context;
gchar *socket_dir;
const GOptionEntry options[] = { const GOptionEntry options[] = {
{ "replace", 'r', 0, G_OPTION_ARG_NONE, &replace, N_("Replace old daemon."), NULL }, { "replace", 'r', 0, G_OPTION_ARG_NONE, &replace, N_("Replace old daemon."), NULL },
{ "no-fuse", 0, 0, G_OPTION_ARG_NONE, &no_fuse, N_("Dont start fuse."), NULL }, { "no-fuse", 0, 0, G_OPTION_ARG_NONE, &no_fuse, N_("Dont start fuse."), NULL },
@ -214,6 +213,11 @@ main (int argc, char *argv[])
if (daemon == NULL) if (daemon == NULL)
return 1; return 1;
/* This is needed for gvfsd-admin to ensure correct ownership. */
socket_dir = gvfs_get_socket_dir ();
g_mkdir (socket_dir, 0700);
g_free (socket_dir);
g_signal_connect (daemon, "shutdown", g_signal_connect (daemon, "shutdown",
G_CALLBACK (daemon_shutdown), loop); G_CALLBACK (daemon_shutdown), loop);

View File

@ -128,7 +128,7 @@ libgvfsdaemon = shared_library(
) )
libgvfsdaemon_dep = declare_dependency( libgvfsdaemon_dep = declare_dependency(
include_directories: include_directories('.'), include_directories: '.',
dependencies: libgvfscommon_dep, dependencies: libgvfscommon_dep,
compile_args: cflags, compile_args: cflags,
link_with: libgvfsdaemon, link_with: libgvfsdaemon,
@ -154,7 +154,7 @@ daemon_main_sources = files(
'daemon-main-generic.c', 'daemon-main-generic.c',
) )
programs = [] programs = {}
mounts = [] mounts = []
schema_data = [] schema_data = []
convert_data = [] convert_data = []
@ -165,7 +165,7 @@ cflags = [
'-DBACKEND_TYPES="localtest", G_VFS_TYPE_BACKEND_LOCALTEST,', '-DBACKEND_TYPES="localtest", G_VFS_TYPE_BACKEND_LOCALTEST,',
] ]
programs += [['gvfsd-localtest', {'sources': files('gvfsbackendlocaltest.c'), 'c_args': cflags}]] programs += {'gvfsd-localtest': {'sources': files('gvfsbackendlocaltest.c'), 'c_args': cflags}}
mounts += ['localtest'] mounts += ['localtest']
sources = files( sources = files(
@ -184,7 +184,7 @@ cflags = [
'-DMAX_JOB_THREADS=10', '-DMAX_JOB_THREADS=10',
] ]
programs += [['gvfsd-ftp', {'sources': sources, 'c_args': cflags}]] programs += {'gvfsd-ftp': {'sources': sources, 'c_args': cflags}}
mounts += ['ftp', 'ftps', 'ftpis'] mounts += ['ftp', 'ftps', 'ftpis']
cflags = [ cflags = [
@ -194,7 +194,7 @@ cflags = [
'-DMAX_JOB_THREADS=10', '-DMAX_JOB_THREADS=10',
] ]
programs += [['gvfsd-trash', {'sources': files('gvfsbackendtrash.c'), 'dependencies': [libtrash_dep], 'c_args': cflags}]] programs += {'gvfsd-trash': {'sources': files('gvfsbackendtrash.c'), 'dependencies': [libtrash_dep], 'c_args': cflags}}
mounts += ['trash'] mounts += ['trash']
cflags = [ cflags = [
@ -205,7 +205,7 @@ cflags = [
'-DMAX_JOB_THREADS=10', '-DMAX_JOB_THREADS=10',
] ]
programs += [['gvfsd-recent', {'sources': files('gvfsbackendrecent.c'), 'c_args': cflags}]] programs += {'gvfsd-recent': {'sources': files('gvfsbackendrecent.c'), 'c_args': cflags}}
mounts += ['recent'] mounts += ['recent']
cflags = [ cflags = [
@ -216,7 +216,7 @@ cflags = [
'-DMAX_JOB_THREADS=1', '-DMAX_JOB_THREADS=1',
] ]
programs += [['gvfsd-computer', {'sources': files('gvfsbackendcomputer.c'), 'dependencies': [gio_unix_dep], 'c_args': cflags}]] programs += {'gvfsd-computer': {'sources': files('gvfsbackendcomputer.c'), 'dependencies': [gio_unix_dep], 'c_args': cflags}}
mounts += ['computer'] mounts += ['computer']
cflags = [ cflags = [
@ -227,7 +227,7 @@ cflags = [
'-DMAX_JOB_THREADS=1', '-DMAX_JOB_THREADS=1',
] ]
programs += [['gvfsd-network', {'sources': files('gvfsbackendnetwork.c'), 'c_args': cflags}]] programs += {'gvfsd-network': {'sources': files('gvfsbackendnetwork.c'), 'c_args': cflags}}
mounts += ['network'] mounts += ['network']
cflags = [ cflags = [
@ -237,7 +237,7 @@ cflags = [
'-DMAX_JOB_THREADS=1', '-DMAX_JOB_THREADS=1',
] ]
programs += [['gvfsd-burn', {'sources': files('gvfsbackendburn.c'), 'dependencies': [gio_unix_dep], 'c_args': cflags}]] programs += {'gvfsd-burn': {'sources': files('gvfsbackendburn.c'), 'dependencies': [gio_unix_dep], 'c_args': cflags}}
mounts += ['burn'] mounts += ['burn']
if enable_sftp if enable_sftp
@ -256,10 +256,10 @@ if enable_sftp
'-DDEFAULT_BACKEND_TYPE=sftp', '-DDEFAULT_BACKEND_TYPE=sftp',
'-DBACKEND_TYPES="sftp", G_VFS_TYPE_BACKEND_SFTP,', '-DBACKEND_TYPES="sftp", G_VFS_TYPE_BACKEND_SFTP,',
'-DMAX_JOB_THREADS=1', '-DMAX_JOB_THREADS=1',
'-DSSH_PROGRAM="@0@"'.format(ssh.path()), '-DSSH_PROGRAM="ssh"',
] ]
programs += [['gvfsd-sftp', {'sources': sources, 'dependencies': deps, 'c_args': cflags}]] programs += {'gvfsd-sftp': {'sources': sources, 'dependencies': deps, 'c_args': cflags}}
mounts += ['sftp'] mounts += ['sftp']
endif endif
@ -273,7 +273,7 @@ if enable_samba
'-DMAX_JOB_THREADS=1', '-DMAX_JOB_THREADS=1',
] ]
programs += [['gvfsd-smb', {'sources': sources, 'dependencies': [smbclient_dep], 'c_args': cflags}]] programs += {'gvfsd-smb': {'sources': sources, 'dependencies': [smbclient_dep], 'c_args': cflags}}
mounts += ['smb'] mounts += ['smb']
schema_data += files('org.gnome.system.smb.gschema.xml') schema_data += files('org.gnome.system.smb.gschema.xml')
convert_data += files('gvfs-smb.convert') convert_data += files('gvfs-smb.convert')
@ -286,7 +286,7 @@ if enable_samba
'-DMOUNTABLE_DBUS_NAME=' + gvfs_namespace + '.mountpoint_smb_browse', '-DMOUNTABLE_DBUS_NAME=' + gvfs_namespace + '.mountpoint_smb_browse',
] ]
programs += [['gvfsd-smb-browse', {'sources': sources + files('gvfsbackendsmbbrowse.c'), 'dependencies': [smbclient_dep], 'c_args': cflags}]] programs += {'gvfsd-smb-browse': {'sources': sources + files('gvfsbackendsmbbrowse.c'), 'dependencies': [smbclient_dep], 'c_args': cflags}}
mounts += ['smb-browse'] mounts += ['smb-browse']
endif endif
@ -305,7 +305,7 @@ if enable_dnssd
'-DMOUNTABLE_DBUS_NAME=' + gvfs_namespace + '.mountpoint_dnssd', '-DMOUNTABLE_DBUS_NAME=' + gvfs_namespace + '.mountpoint_dnssd',
] ]
programs += [['gvfsd-dnssd', {'sources': files('gvfsbackenddnssd.c'), 'dependencies': deps, 'c_args': cflags}]] programs += {'gvfsd-dnssd': {'sources': files('gvfsbackenddnssd.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['dns-sd'] mounts += ['dns-sd']
schema_data += files('org.gnome.system.dns_sd.gschema.xml') schema_data += files('org.gnome.system.dns_sd.gschema.xml')
convert_data += files('gvfs-dns-sd.convert') convert_data += files('gvfs-dns-sd.convert')
@ -320,7 +320,7 @@ if enable_archive
'-DBACKEND_USES_GVFS=1', '-DBACKEND_USES_GVFS=1',
] ]
programs += [['gvfsd-archive', {'sources': files('gvfsbackendarchive.c'), 'dependencies': [libarchive_dep], 'c_args': cflags}]] programs += {'gvfsd-archive': {'sources': files('gvfsbackendarchive.c'), 'dependencies': [libarchive_dep], 'c_args': cflags}}
mounts += ['archive'] mounts += ['archive']
endif endif
@ -337,7 +337,7 @@ if enable_cdda
'-DMAX_JOB_THREADS=1', '-DMAX_JOB_THREADS=1',
] ]
programs += [['gvfsd-cdda', {'sources': files('gvfsbackendcdda.c'), 'dependencies': deps, 'c_args': cflags}]] programs += {'gvfsd-cdda': {'sources': files('gvfsbackendcdda.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['cdda'] mounts += ['cdda']
endif endif
@ -354,27 +354,27 @@ if enable_admin
'-DMOUNTABLE_DBUS_NAME=' + gvfs_namespace + '.mountpoint_admin', '-DMOUNTABLE_DBUS_NAME=' + gvfs_namespace + '.mountpoint_admin',
] ]
programs += [['gvfsd-admin', {'sources': files('gvfsbackendadmin.c'), 'dependencies': deps, 'c_args': cflags}]] programs += {'gvfsd-admin': {'sources': files('gvfsbackendadmin.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['admin'] mounts += ['admin']
policy = gvfs_namespace + '.file-operations.policy' policy = gvfs_namespace + '.file-operations.policy'
policy_in = configure_file(
input: policy + '.in.in',
output: '@BASENAME@',
configuration: service_conf,
)
i18n.merge_file( i18n.merge_file(
input: policy_in, input: configure_file(
input: policy + '.in.in',
output: '@BASENAME@',
configuration: service_conf,
),
output: '@BASENAME@', output: '@BASENAME@',
po_dir: po_dir, po_dir: po_dir,
install: true, install: true,
install_dir: gvfs_datadir / 'polkit-1/actions', install_dir: gvfs_datadir / 'polkit-1/actions',
) )
install_data( configure_file(
gvfs_namespace + '.file-operations.rules', input: gvfs_namespace + '.file-operations.rules.in',
output: '@BASENAME@',
configuration: {'PRIVILEGED_GROUP': privileged_group},
install_dir: gvfs_datadir / 'polkit-1/rules.d', install_dir: gvfs_datadir / 'polkit-1/rules.d',
) )
endif endif
@ -391,7 +391,7 @@ if enable_google
'-DBACKEND_TYPES="google-drive", G_VFS_TYPE_BACKEND_GOOGLE,', '-DBACKEND_TYPES="google-drive", G_VFS_TYPE_BACKEND_GOOGLE,',
] ]
programs += [['gvfsd-google', {'sources': files('gvfsbackendgoogle.c'), 'dependencies': deps, 'c_args': cflags}]] programs += {'gvfsd-google': {'sources': files('gvfsbackendgoogle.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['google'] mounts += ['google']
endif endif
@ -409,7 +409,7 @@ if enable_gphoto2
'-DMAX_JOB_THREADS=1', '-DMAX_JOB_THREADS=1',
] ]
programs += [['gvfsd-gphoto2', {'sources': files('gvfsbackendgphoto2.c'), 'dependencies': deps, 'c_args': cflags}]] programs += {'gvfsd-gphoto2': {'sources': files('gvfsbackendgphoto2.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['gphoto2'] mounts += ['gphoto2']
endif endif
@ -430,7 +430,7 @@ if enable_mtp
deps += libusb_dep deps += libusb_dep
endif endif
programs += [['gvfsd-mtp', {'sources': files('gvfsbackendmtp.c'), 'dependencies': deps, 'c_args': cflags}]] programs += {'gvfsd-mtp': {'sources': files('gvfsbackendmtp.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['mtp'] mounts += ['mtp']
endif endif
@ -453,7 +453,7 @@ if enable_http
'-DMOUNTABLE_DBUS_NAME=' + gvfs_namespace + '.mountpoint_http', '-DMOUNTABLE_DBUS_NAME=' + gvfs_namespace + '.mountpoint_http',
] ]
programs += [['gvfsd-http', {'sources': sources, 'dependencies': deps, 'c_args': cflags}]] programs += {'gvfsd-http': {'sources': sources, 'dependencies': deps, 'c_args': cflags}}
mounts += ['http'] mounts += ['http']
cflags = [ cflags = [
@ -472,7 +472,7 @@ if enable_http
cflags += '-DBACKEND_TYPES="dav", G_VFS_TYPE_BACKEND_DAV,' cflags += '-DBACKEND_TYPES="dav", G_VFS_TYPE_BACKEND_DAV,'
endif endif
programs += [['gvfsd-dav', {'sources': sources + files('gvfsbackenddav.c'), 'dependencies': deps, 'c_args': cflags}]] programs += {'gvfsd-dav': {'sources': sources + files('gvfsbackenddav.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['dav'] mounts += ['dav']
endif endif
@ -490,7 +490,7 @@ if enable_afc
'-DBACKEND_USES_GVFS=1', '-DBACKEND_USES_GVFS=1',
] ]
programs += [['gvfsd-afc', {'sources': files('gvfsbackendafc.c'), 'dependencies': deps, 'c_args': cflags}]] programs += {'gvfsd-afc': {'sources': files('gvfsbackendafc.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['afc'] mounts += ['afc']
endif endif
@ -515,7 +515,7 @@ if enable_afp
'-DMAX_JOB_THREADS=1', '-DMAX_JOB_THREADS=1',
] ]
programs += [['gvfsd-afp', {'sources': common_sources + files('gvfsbackendafp.c'), 'dependencies': deps, 'c_args': cflags}]] programs += {'gvfsd-afp': {'sources': common_sources + files('gvfsbackendafp.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['afp'] mounts += ['afp']
cflags = [ cflags = [
@ -525,7 +525,7 @@ if enable_afp
'-DMAX_JOB_THREADS=1', '-DMAX_JOB_THREADS=1',
] ]
programs += [['gvfsd-afp-browse', {'sources': common_sources + files('gvfsbackendafpbrowse.c'), 'dependencies': deps, 'c_args': cflags}]] programs += {'gvfsd-afp-browse': {'sources': common_sources + files('gvfsbackendafpbrowse.c'), 'dependencies': deps, 'c_args': cflags}}
mounts += ['afp-browse'] mounts += ['afp-browse']
endif endif
@ -537,12 +537,11 @@ if enable_nfs
'-DMAX_JOB_THREADS=1', '-DMAX_JOB_THREADS=1',
] ]
programs += [['gvfsd-nfs', {'sources': files('gvfsbackendnfs.c'), 'dependencies': [libnfs_dep], 'c_args': cflags}]] programs += {'gvfsd-nfs': {'sources': files('gvfsbackendnfs.c'), 'dependencies': [libnfs_dep], 'c_args': cflags}}
mounts += ['nfs'] mounts += ['nfs']
endif endif
foreach program: programs foreach program, options: programs
options = program[1]
kwargs = { kwargs = {
'sources': daemon_main_sources + options.get('sources', []), 'sources': daemon_main_sources + options.get('sources', []),
'dependencies': [libgvfsdaemon_dep] + options.get('dependencies', []), 'dependencies': [libgvfsdaemon_dep] + options.get('dependencies', []),
@ -550,7 +549,7 @@ foreach program: programs
} }
executable( executable(
program[0], program,
include_directories: top_inc, include_directories: top_inc,
kwargs: kwargs, kwargs: kwargs,
install: true, install: true,

View File

@ -415,6 +415,30 @@ child_watch_cb (GPid pid,
gint status, gint status,
gpointer user_data) gpointer user_data)
{ {
MountData *data = user_data;
GError *error = NULL;
gint code = 0;
if (!g_spawn_check_wait_status (status, &error))
{
if (error->domain == G_SPAWN_EXIT_ERROR)
code = error->code;
g_clear_error (&error);
}
/* GVfs daemons always exit with 0, but gvfsd-admin is spawned over pkexec,
* which can fail when the authentication dialog is dismissed for example.
*/
if (code == 126 || code == 127)
{
error = g_error_new_literal (G_IO_ERROR,
G_IO_ERROR_PERMISSION_DENIED,
_("Permission denied"));
mount_finish (data, error);
g_error_free (error);
}
g_spawn_close_pid (pid); g_spawn_close_pid (pid);
} }
@ -485,7 +509,7 @@ spawn_mount (MountData *data)
} }
else else
{ {
g_child_watch_add (pid, child_watch_cb, NULL); g_child_watch_add (pid, child_watch_cb, data);
} }
g_strfreev (argv); g_strfreev (argv);

View File

@ -1,13 +1,13 @@
// Allows users belonging to wheel group to start gvfsd-admin without // Allows users belonging to privileged group to start gvfsd-admin without
// authorization. This prevents redundant password prompt when starting // authorization. This prevents redundant password prompt when starting
// gvfsd-admin. The gvfsd-admin causes another password prompts to be shown // gvfsd-admin. The gvfsd-admin causes another password prompt to be shown
// for each client process using the different action id and for the subject // for each client process using the different action id and for the subject
// based on the client process. // based on the client process.
polkit.addRule(function(action, subject) { polkit.addRule(function(action, subject) {
if ((action.id == "org.gtk.vfs.file-operations-helper") && if ((action.id == "org.gtk.vfs.file-operations-helper") &&
subject.local && subject.local &&
subject.active && subject.active &&
subject.isInGroup ("sudo")) { subject.isInGroup ("@PRIVILEGED_GROUP@")) {
return polkit.Result.YES; return polkit.Result.YES;
} }
}); });

View File

@ -272,10 +272,10 @@ dir_watch_free (DirWatch *watch)
if (watch != NULL) if (watch != NULL)
{ {
if (watch->parent_monitor) if (watch->parent_monitor)
{ {
g_file_monitor_cancel (watch->parent_monitor); g_file_monitor_cancel (watch->parent_monitor);
g_object_unref (watch->parent_monitor); g_object_unref (watch->parent_monitor);
} }
g_object_unref (watch->directory); g_object_unref (watch->directory);
g_object_unref (watch->topdir); g_object_unref (watch->topdir);

View File

@ -211,6 +211,34 @@ trash_mount_remove (TrashMount **mount_ptr)
g_slice_free (TrashMount, mount); g_slice_free (TrashMount, mount);
} }
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 void static void
trash_watcher_remount (TrashWatcher *watcher) trash_watcher_remount (TrashWatcher *watcher)
{ {
@ -229,7 +257,7 @@ trash_watcher_remount (TrashWatcher *watcher)
{ {
int result; int result;
if (new && g_unix_mount_is_system_internal (new->data)) if (new && ignore_trash_mount (new->data))
{ {
g_unix_mount_free (new->data); g_unix_mount_free (new->data);
new = new->next; new = new->next;

6
debian/changelog vendored
View File

@ -1,8 +1,8 @@
gvfs (1.44.1-ok6) yangtze; urgency=medium gvfs (1.50.3-ok1~0407) yangtze; urgency=medium
* update version info * New upstream release.
-- luzhiping <luzhiping@kylinos.cn> Mon, 22 Aug 2022 13:58:48 +0800 -- Yue Lan <lanyue@kylinos.cn> Fri, 07 Apr 2023 11:40:43 +0800
gvfs (1.44.1-ok5~0629) yangtze; urgency=medium gvfs (1.44.1-ok5~0629) yangtze; urgency=medium

2
debian/control vendored
View File

@ -5,7 +5,7 @@
Source: gvfs Source: gvfs
Section: gnome Section: gnome
Priority: optional Priority: optional
Maintainer: Openkylin Developers <packaging@lists.openkylin.top> Maintainer: Debian GNOME Maintainers <pkg-gnome-maintainers@lists.alioth.debian.org>
Uploaders: Iain Lane <laney@debian.org>, Jeremy Bicha <jbicha@debian.org>, Laurent Bigonville <bigon@debian.org> Uploaders: Iain Lane <laney@debian.org>, Jeremy Bicha <jbicha@debian.org>, Laurent Bigonville <bigon@debian.org>
Build-Depends: debhelper-compat (= 12), Build-Depends: debhelper-compat (= 12),
dh-exec (>= 0.13), dh-exec (>= 0.13),

View File

@ -10,7 +10,7 @@
<description xml:lang="en">GVfs is a userspace virtual filesystem implementation for GIO (a library available in GLib). GVfs comes with a set of backends, including trash support, SFTP, SMB, HTTP, DAV, and many others. GVfs also contains modules for GIO that implement volume monitors and persistent metadata storage. There is also FUSE support that provides limited access to the GVfs filesystems for applications not using GIO.</description> <description xml:lang="en">GVfs is a userspace virtual filesystem implementation for GIO (a library available in GLib). GVfs comes with a set of backends, including trash support, SFTP, SMB, HTTP, DAV, and many others. GVfs also contains modules for GIO that implement volume monitors and persistent metadata storage. There is also FUSE support that provides limited access to the GVfs filesystems for applications not using GIO.</description>
<homepage rdf:resource="https://wiki.gnome.org/Projects/gvfs" /> <homepage rdf:resource="https://wiki.gnome.org/Projects/gvfs" />
<mailing-list rdf:resource="http://mail.gnome.org/mailman/listinfo/gvfs-list" /> <mailing-list rdf:resource="https://discourse.gnome.org" />
<download-page rdf:resource="http://download.gnome.org/sources/gvfs/" /> <download-page rdf:resource="http://download.gnome.org/sources/gvfs/" />
<bug-database rdf:resource="https://gitlab.gnome.org/GNOME/gvfs/issues/" /> <bug-database rdf:resource="https://gitlab.gnome.org/GNOME/gvfs/issues/" />
<category rdf:resource="http://api.gnome.org/doap-extensions#core" /> <category rdf:resource="http://api.gnome.org/doap-extensions#core" />
@ -20,7 +20,7 @@
<maintainer> <maintainer>
<foaf:Person> <foaf:Person>
<foaf:name>Ondrej Holy</foaf:name> <foaf:name>Ondrej Holy</foaf:name>
<foaf:mbox rdf:resource="mailto:oholy@redhat.com" /> <foaf:mbox rdf:resource="mailto:oholy@gnome.org" />
<gnome:userid>oholy</gnome:userid> <gnome:userid>oholy</gnome:userid>
</foaf:Person> </foaf:Person>
</maintainer> </maintainer>

View File

@ -14,20 +14,20 @@ xsltproc_cmd = [
'@INPUT@', '@INPUT@',
] ]
mans = [ mans = {
['gvfs', '7'], 'gvfs': '7',
['gvfsd', '1'], 'gvfsd': '1',
['gvfsd-fuse', '1'], 'gvfsd-fuse': '1',
['gvfsd-metadata', '1'], 'gvfsd-metadata': '1',
] }
foreach man: mans foreach program, section: mans
custom_target( custom_target(
man[0] + man[1], program + section,
input: man[0] + '.xml', input: program + '.xml',
output: '@BASENAME@.' + man[1], output: '@BASENAME@.' + section,
command: xsltproc_cmd, command: xsltproc_cmd,
install: true, install: true,
install_dir: gvfs_mandir / ('man' + man[1]), install_dir: gvfs_mandir / ('man' + section),
) )
endforeach endforeach

View File

@ -1,9 +1,9 @@
project( project(
'gvfs', 'c', 'gvfs', 'c',
version: '1.44.1', version: '1.50.3',
license: 'LGPL2+', license: 'LGPL2+',
default_options: 'buildtype=debugoptimized', default_options: 'buildtype=debugoptimized',
meson_version: '>= 0.50.0', meson_version: '>= 0.56.0',
) )
gvfs_name = meson.project_name() gvfs_name = meson.project_name()
@ -44,16 +44,16 @@ cc = meson.get_compiler('c')
config_h = configuration_data() config_h = configuration_data()
# defines # defines
set_defines = [ set_defines = {
# package # package
['PACKAGE_STRING', '@0@ @1@'.format(gvfs_name, gvfs_version)], 'PACKAGE_STRING': '@0@ @1@'.format(gvfs_name, gvfs_version),
['VERSION', gvfs_version], 'VERSION': gvfs_version,
# i18n # i18n
['GETTEXT_PACKAGE', gvfs_name], 'GETTEXT_PACKAGE': gvfs_name,
] }
foreach define: set_defines foreach define, value: set_defines
config_h.set_quoted(define[0], define[1]) config_h.set_quoted(define, value)
endforeach endforeach
# Globally define_GNU_SOURCE and therefore enable the GNU extensions # Globally define_GNU_SOURCE and therefore enable the GNU extensions
@ -179,17 +179,17 @@ foreach name: ['mkdev', 'sysmacros']
endforeach endforeach
# types # types
check_types = [ check_types = {
# type, fallback type # type, fallback type
['gid_t', 'int'], 'gid_t': 'int',
['pid_t', 'int'], 'pid_t': 'int',
['size_t', 'unsigned int'], 'size_t': 'unsigned int',
['uid_t', 'int'], 'uid_t': 'int',
] }
foreach type: check_types foreach type, value: check_types
if not cc.has_type(type[0], prefix: '#include<sys/types.h>') if not cc.has_type(type, prefix: '#include<sys/types.h>')
config_h.set(type[0], type[1]) config_h.set(type, value)
endif endif
endforeach endforeach
@ -231,7 +231,7 @@ have_version_script = cc.has_link_argument('@0@,@1@'.format(version_script_ldfla
gio_dep = dependency('gio-2.0') gio_dep = dependency('gio-2.0')
gio_unix_dep = dependency('gio-unix-2.0') gio_unix_dep = dependency('gio-unix-2.0')
glib_dep = dependency('glib-2.0', version: '>= 2.57.2') glib_dep = dependency('glib-2.0', version: '>= 2.70.0')
gobject_dep = dependency('gobject-2.0') gobject_dep = dependency('gobject-2.0')
gsettings_desktop_schemas_dep = dependency('gsettings-desktop-schemas', version: '>= 3.33.0') gsettings_desktop_schemas_dep = dependency('gsettings-desktop-schemas', version: '>= 3.33.0')
@ -249,23 +249,23 @@ endif
config_h.set('HAVE_GCRYPT', enable_gcrypt) config_h.set('HAVE_GCRYPT', enable_gcrypt)
# *** Check for dbus service dir *** # *** Check for dbus service dir ***
dbus_session_bus_services_dir = dependency('dbus-1').get_pkgconfig_variable( dbus_session_bus_services_dir = dependency('dbus-1').get_variable(
'session_bus_services_dir', pkgconfig: 'session_bus_services_dir',
define_variable: ['datadir', gvfs_prefix / gvfs_datadir], pkgconfig_define: ['datadir', gvfs_prefix / gvfs_datadir],
) )
dbus_service_in = files('dbus.service.in') dbus_service_in = files('dbus.service.in')
# *** Check for giomoduledir and schemasdir *** # *** Check for giomoduledir and schemasdir ***
gio_giomoduledir = gio_dep.get_pkgconfig_variable( gio_giomoduledir = gio_dep.get_variable(
'giomoduledir', pkgconfig: 'giomoduledir',
define_variable: ['libdir', gvfs_prefix / gvfs_libdir], pkgconfig_define: ['libdir', gvfs_prefix / gvfs_libdir],
) )
gio_schemasdir = gio_dep.get_pkgconfig_variable( gio_schemasdir = gio_dep.get_variable(
'schemasdir', pkgconfig: 'schemasdir',
define_variable: ['datadir', gvfs_prefix / gvfs_datadir], pkgconfig_define: ['datadir', gvfs_prefix / gvfs_datadir],
default: gvfs_prefix / gvfs_datadir / 'glib-2.0/schemas', default_value: gvfs_prefix / gvfs_datadir / 'glib-2.0/schemas',
) )
# *** Check for systemd options *** # *** Check for systemd options ***
@ -281,12 +281,12 @@ if install_systemd_systemduserunitdir or install_systemd_tmpfilesdir
if install_systemd_systemduserunitdir and systemd_systemduserunitdir == '' if install_systemd_systemduserunitdir and systemd_systemduserunitdir == ''
assert(systemd_dep.found(), 'systemd required but not found, please provide a valid systemd user unit dir or disable it') assert(systemd_dep.found(), 'systemd required but not found, please provide a valid systemd user unit dir or disable it')
systemd_systemduserunitdir = systemd_dep.get_pkgconfig_variable('systemduserunitdir') systemd_systemduserunitdir = systemd_dep.get_variable(pkgconfig: 'systemduserunitdir', pkgconfig_define: ['prefix', gvfs_prefix])
endif endif
if install_systemd_tmpfilesdir and systemd_tmpfilesdir == '' if install_systemd_tmpfilesdir and systemd_tmpfilesdir == ''
assert(systemd_dep.found(), 'systemd not found, if you use opentmpfiles please provide a valid systemd user unit dir or disable it') assert(systemd_dep.found(), 'systemd not found, if you use opentmpfiles please provide a valid systemd user unit dir or disable it')
systemd_tmpfilesdir = systemd_dep.get_pkgconfig_variable('tmpfilesdir') systemd_tmpfilesdir = systemd_dep.get_variable(pkgconfig: 'tmpfilesdir', pkgconfig_define: ['prefix', gvfs_prefix])
endif endif
endif endif
endif endif
@ -299,17 +299,18 @@ endif
config_h.set('HAVE_GCR', enable_gcr) config_h.set('HAVE_GCR', enable_gcr)
# *** Check if we should build with admin backend *** # *** Check if we should build with admin backend ***
privileged_group = get_option('privileged_group')
enable_admin = get_option('admin') enable_admin = get_option('admin')
if enable_admin if enable_admin
libcap_dep = dependency('libcap') libcap_dep = dependency('libcap')
polkit_gobject_dep = dependency('polkit-gobject-1') polkit_gobject_dep = dependency('polkit-gobject-1', version: '>= 0.114')
endif endif
# *** Check if we should build with http backend *** # *** Check if we should build with http backend ***
enable_http = get_option('http') enable_http = get_option('http')
if enable_http if enable_http
assert(have_libxml, 'http required but libxml-2.0 not found') assert(have_libxml, 'http required but libxml-2.0 not found')
libsoup_dep = dependency('libsoup-2.4', version: '>= 2.58.0') libsoup_dep = dependency('libsoup-3.0', version: '>= 3.0.0')
endif endif
# *** Check if we should build with DNS-SD backend *** # *** Check if we should build with DNS-SD backend ***
@ -330,7 +331,7 @@ config_h.set('HAVE_GUDEV', enable_gudev)
# *** Check for FUSE *** # *** Check for FUSE ***
enable_fuse = get_option('fuse') enable_fuse = get_option('fuse')
if enable_fuse if enable_fuse
fuse_dep = dependency('fuse', version: '>= 2.8.0') fuse_dep = dependency('fuse3', version: '>= 3.0.0')
endif endif
config_h.set('HAVE_FUSE', enable_fuse) config_h.set('HAVE_FUSE', enable_fuse)
@ -357,7 +358,10 @@ config_h.set('HAVE_LOGIND', enable_logind)
enable_afc = get_option('afc') enable_afc = get_option('afc')
if enable_afc if enable_afc
libimobiledevice_dep = dependency('libimobiledevice-1.0', version: '>= 1.2') libimobiledevice_dep = dependency('libimobiledevice-1.0', version: '>= 1.2')
libplist_dep = dependency('libplist', version: '>= 0.15') libplist_dep = dependency('libplist-2.0', required: false)
if not libplist_dep.found()
libplist_dep = dependency('libplist', version: '>= 0.15')
endif
endif endif
# *** Check if we should build with GOA volume monitor *** # *** Check if we should build with GOA volume monitor ***
@ -416,7 +420,7 @@ enable_google = get_option('google')
if enable_google if enable_google
assert(enable_goa, 'Google backend requested but GOA is required') assert(enable_goa, 'Google backend requested but GOA is required')
libgdata_dep = dependency('libgdata', version: '>= 0.17.11') libgdata_dep = dependency('libgdata', version: '>= 0.18.0')
endif endif
# *** Check for gphoto2 *** # *** Check for gphoto2 ***
@ -453,10 +457,6 @@ endif
# *** SFTP backend *** # *** SFTP backend ***
enable_sftp = get_option('sftp') enable_sftp = get_option('sftp')
if enable_sftp
ssh = find_program('ssh', required: false)
assert(ssh.found(), 'SFTP backend requested but a ssh client is required')
endif
# *** Enable development utils *** # *** Enable development utils ***
enable_devel_utils = get_option('devel_utils') enable_devel_utils = get_option('devel_utils')
@ -487,37 +487,43 @@ meson.add_install_script(
gio_giomoduledir, gio_giomoduledir,
) )
output = gvfs_name + ' ' + gvfs_version + ' configuration summary:\n' summary({
output += '\n' 'systemduserunitdir': systemd_systemduserunitdir,
output += ' systemduserunitdir: ' + systemd_systemduserunitdir + '\n' 'tmpfilesdir': systemd_tmpfilesdir,
output += ' tmpfilesdir: ' + systemd_tmpfilesdir + '\n' 'privileged_group': privileged_group,
output += '\n' }, section: 'Configuration')
output += ' admin: ' + enable_admin.to_string() + '\n'
output += ' afc: ' + enable_afc.to_string() + '\n' summary({
output += ' afp: ' + enable_afp.to_string() + '\n' 'admin': enable_admin,
output += ' archive: ' + enable_archive.to_string() + '\n' 'afc': enable_afc,
output += ' cdda: ' + enable_cdda.to_string() + '\n' 'afp': enable_afp,
output += ' dnssd: ' + enable_dnssd.to_string() + '\n' 'archive': enable_archive,
output += ' goa: ' + enable_goa.to_string() + '\n' 'cdda': enable_cdda,
output += ' google: ' + enable_google.to_string() + '\n' 'dnssd': enable_dnssd,
output += ' gphoto2: ' + enable_gphoto2.to_string() + '\n' 'goa': enable_goa,
output += ' http: ' + enable_http.to_string() + '\n' 'google': enable_google,
output += ' mtp: ' + enable_mtp.to_string() + '\n' 'gphoto2': enable_gphoto2,
output += ' nfs: ' + enable_nfs.to_string() + '\n' 'http': enable_http,
output += ' sftp: ' + enable_sftp.to_string() + '\n' 'mtp': enable_mtp,
output += ' smb: ' + enable_samba.to_string() + '\n' 'nfs': enable_nfs,
output += ' udisks2: ' + enable_udisks2.to_string() + '\n' 'sftp': enable_sftp,
output += '\n' 'smb': enable_samba,
output += ' bluray: ' + enable_bluray.to_string() + '\n' 'udisks2': enable_udisks2,
output += ' fuse: ' + enable_fuse.to_string() + '\n' }, section: 'Backends')
output += ' gcr: ' + enable_gcr.to_string() + '\n'
output += ' gcrypt: ' + enable_gcrypt.to_string() + '\n' summary({
output += ' gudev: ' + enable_gudev.to_string() + '\n' 'bluray': enable_bluray,
output += ' keyring: ' + enable_keyring.to_string() + '\n' 'fuse': enable_fuse,
output += ' logind: ' + enable_logind.to_string() + '\n' 'gcr': enable_gcr,
output += ' libusb: ' + enable_libusb.to_string() + '\n' 'gcrypt': enable_gcrypt,
output += '\n' 'gudev': enable_gudev,
output += ' devel_utils: ' + enable_devel_utils.to_string() + '\n' 'keyring': enable_keyring,
output += ' installed_tests: ' + enable_installed_tests.to_string() + '\n' 'logind': enable_logind,
output += ' man: ' + enable_man.to_string() + '\n' 'libusb': enable_libusb,
message(output) }, section: 'Dependencies')
summary({
'devel_utils': enable_devel_utils,
'installed_tests': enable_installed_tests,
'man': enable_man,
})

View File

@ -1,5 +1,6 @@
option('systemduserunitdir', type: 'string', value: '', description: 'custom directory for systemd user units, or \'no\' to disable') option('systemduserunitdir', type: 'string', value: '', description: 'custom directory for systemd user units, or \'no\' to disable')
option('tmpfilesdir', type: 'string', value: '', description: 'custom directory for tmpfiles.d config files, or \'no\' to disable') option('tmpfilesdir', type: 'string', value: '', description: 'custom directory for tmpfiles.d config files, or \'no\' to disable')
option('privileged_group', type: 'string', value: 'wheel', description: 'custom name for group that has elevated permissions')
option('admin', type: 'boolean', value: true, description: 'build with admin backend') option('admin', type: 'boolean', value: true, description: 'build with admin backend')
option('afc', type: 'boolean', value: true, description: 'build with afc backend and volume monitor') option('afc', type: 'boolean', value: true, description: 'build with afc backend and volume monitor')

View File

@ -45,6 +45,10 @@
<arg type='u' name='minor' direction='in'/> <arg type='u' name='minor' direction='in'/>
<arg type='s' name='tree' direction='out'/> <arg type='s' name='tree' direction='out'/>
</method> </method>
<signal name="AttributeChanged">
<arg type='s' name='tree_path'/>
<arg type='s' name='file_path'/>
</signal>
</interface> </interface>
</node> </node>

View File

@ -1,7 +1,9 @@
[Unit] [Unit]
Description=Virtual filesystem metadata service Description=Virtual filesystem metadata service
PartOf=graphical-session.target
[Service] [Service]
ExecStart=@libexecdir@/gvfsd-metadata ExecStart=@libexecdir@/gvfsd-metadata
Type=dbus Type=dbus
BusName=org.gtk.vfs.Metadata BusName=org.gtk.vfs.Metadata
Slice=session.slice

View File

@ -47,6 +47,7 @@ sources = files(
deps = [ deps = [
gio_dep, gio_dep,
glib_dep, glib_dep,
libgvfscommon_dep,
] ]
cflags = [ cflags = [
@ -58,7 +59,7 @@ cflags = [
libmetadata = static_library( libmetadata = static_library(
'metadata', 'metadata',
sources: sources + [dbus_sources], sources: sources + [dbus_sources],
include_directories: [top_inc, common_inc], include_directories: top_inc,
dependencies: deps + [gio_unix_dep], dependencies: deps + [gio_unix_dep],
c_args: cflags, c_args: cflags,
pic: true, pic: true,
@ -71,10 +72,7 @@ libmetadata_dep = declare_dependency(
link_with: libmetadata, link_with: libmetadata,
) )
deps = [ deps = [libmetadata_dep]
libgvfscommon_dep,
libmetadata_dep,
]
if enable_gudev if enable_gudev
deps += gudev_dep deps += gudev_dep
@ -104,10 +102,7 @@ if enable_devel_utils
app, app,
app + '.c', app + '.c',
include_directories: top_inc, include_directories: top_inc,
dependencies: [ dependencies: libmetadata_dep,
libgvfscommon_dep,
libmetadata_dep,
],
c_args: cflags, c_args: cflags,
) )
endforeach endforeach

View File

@ -43,6 +43,7 @@
#define WRITEOUT_TIMEOUT_SECS 60 #define WRITEOUT_TIMEOUT_SECS 60
#define WRITEOUT_TIMEOUT_SECS_NFS 15 #define WRITEOUT_TIMEOUT_SECS_NFS 15
#define WRITEOUT_TIMEOUT_SECS_DBUS 1
typedef struct { typedef struct {
char *filename; char *filename;
@ -50,11 +51,19 @@ typedef struct {
guint writeout_timeout; guint writeout_timeout;
} TreeInfo; } TreeInfo;
typedef struct {
gchar *treefile;
gchar *path;
GVfsMetadata *object;
guint timeout_id;
} BusNotificationInfo;
static GHashTable *tree_infos = NULL; static GHashTable *tree_infos = NULL;
static GVfsMetadata *skeleton = NULL; static GVfsMetadata *skeleton = NULL;
#ifdef HAVE_GUDEV #ifdef HAVE_GUDEV
static GUdevClient *gudev_client = NULL; static GUdevClient *gudev_client = NULL;
#endif #endif
static GList *dbus_notification_list = NULL;
static void static void
tree_info_free (TreeInfo *info) tree_info_free (TreeInfo *info)
@ -105,8 +114,78 @@ flush_single (const gchar *filename,
} }
static void static void
flush_all () free_bus_notification_info (BusNotificationInfo *info)
{ {
dbus_notification_list = g_list_remove (dbus_notification_list,
info);
g_object_unref (info->object);
g_source_remove (info->timeout_id);
g_free (info->path);
g_free (info->treefile);
g_free (info);
}
static gboolean
notify_attribute_change (gpointer data)
{
BusNotificationInfo *info;
info = (BusNotificationInfo *) data;
gvfs_metadata_emit_attribute_changed (info->object,
info->treefile,
info->path);
free_bus_notification_info (info);
return G_SOURCE_REMOVE;
}
static void
emit_attribute_change (GVfsMetadata *object,
const gchar *treefile,
const gchar *path)
{
GList *iter;
BusNotificationInfo *info;
for (iter = dbus_notification_list; iter != NULL; iter = iter->next)
{
info = iter->data;
if (g_str_equal (info->treefile, treefile) &&
g_str_equal (info->path, path))
{
break;
}
}
if (iter == NULL)
{
info = g_new0 (BusNotificationInfo, 1);
info->treefile = g_strdup (treefile);
info->path = g_strdup (path);
info->object = g_object_ref (object);
dbus_notification_list = g_list_prepend (dbus_notification_list,
info);
}
else
{
g_source_remove (info->timeout_id);
}
info->timeout_id = g_timeout_add_seconds (WRITEOUT_TIMEOUT_SECS_DBUS,
notify_attribute_change,
info);
}
static void
flush_all (gboolean send_pending_notifications)
{
BusNotificationInfo *info;
while (dbus_notification_list != NULL)
{
info = (BusNotificationInfo *) dbus_notification_list->data;
if (send_pending_notifications)
notify_attribute_change (info);
else
free_bus_notification_info (info);
}
g_hash_table_foreach (tree_infos, (GHFunc) flush_single, NULL); g_hash_table_foreach (tree_infos, (GHFunc) flush_single, NULL);
} }
@ -222,6 +301,7 @@ handle_set (GVfsMetadata *object,
} }
else else
{ {
emit_attribute_change (object, arg_treefile, arg_path);
gvfs_metadata_complete_set (object, invocation); gvfs_metadata_complete_set (object, invocation);
} }
@ -257,6 +337,7 @@ handle_remove (GVfsMetadata *object,
return TRUE; return TRUE;
} }
emit_attribute_change (object, arg_treefile, arg_path);
tree_info_schedule_writeout (info); tree_info_schedule_writeout (info);
gvfs_metadata_complete_remove (object, invocation); gvfs_metadata_complete_remove (object, invocation);
@ -297,6 +378,8 @@ handle_move (GVfsMetadata *object,
/* Remove source if copy succeeded (ignoring errors) */ /* Remove source if copy succeeded (ignoring errors) */
meta_tree_remove (info->tree, arg_path); meta_tree_remove (info->tree, arg_path);
emit_attribute_change (object, arg_treefile, arg_path);
emit_attribute_change (object, arg_treefile, arg_dest_path);
tree_info_schedule_writeout (info); tree_info_schedule_writeout (info);
gvfs_metadata_complete_move (object, invocation); gvfs_metadata_complete_move (object, invocation);
@ -344,7 +427,7 @@ on_name_lost (GDBusConnection *connection,
GMainLoop *loop = user_data; GMainLoop *loop = user_data;
/* means that someone has claimed our name (we allow replacement) */ /* means that someone has claimed our name (we allow replacement) */
flush_all (); flush_all (TRUE);
g_main_loop_quit (loop); g_main_loop_quit (loop);
} }
@ -357,7 +440,7 @@ on_connection_closed (GDBusConnection *connection,
GMainLoop *loop = user_data; GMainLoop *loop = user_data;
/* session bus died */ /* session bus died */
flush_all (); flush_all (FALSE);
g_main_loop_quit (loop); g_main_loop_quit (loop);
} }

View File

@ -1234,7 +1234,7 @@ meta_builder_write (MetaBuilder *builder,
data = mmap (NULL, RANDOM_TAG_OFFSET + 4, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0); data = mmap (NULL, RANDOM_TAG_OFFSET + 4, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, 0);
if (data) if (data != MAP_FAILED)
{ {
old_tag = GUINT32_FROM_BE (*(guint32 *)(data + RANDOM_TAG_OFFSET)); old_tag = GUINT32_FROM_BE (*(guint32 *)(data + RANDOM_TAG_OFFSET));
*(guint32 *)(data + ROTATED_OFFSET) = 0xffffffff; *(guint32 *)(data + ROTATED_OFFSET) = 0xffffffff;

View File

@ -170,7 +170,7 @@ GVfsMetadata *
meta_tree_get_metadata_proxy () meta_tree_get_metadata_proxy ()
{ {
static GVfsMetadata *proxy = NULL; static GVfsMetadata *proxy = NULL;
static volatile gsize initialized = 0; static gsize initialized = 0;
if (g_once_init_enter (&initialized)) if (g_once_init_enter (&initialized))
{ {
@ -451,15 +451,7 @@ meta_tree_init (MetaTree *tree)
if (memcmp (tree->header->magic, MAGIC, MAGIC_LEN) != 0) if (memcmp (tree->header->magic, MAGIC, MAGIC_LEN) != 0)
{ {
g_warning ("can't init metadata tree %s: wrong magic", tree->filename); g_warning ("can't init metadata tree %s: wrong magic", tree->filename);
if (!tree->for_write) goto err;
goto err;
meta_tree_clear (tree);
if (g_unlink (tree->filename) != 0)
goto err;
goto retry;
} }
if (tree->header->major != MAJOR_VERSION) if (tree->header->major != MAJOR_VERSION)

View File

@ -1,7 +1,9 @@
[Unit] [Unit]
Description=Virtual filesystem service - Apple File Conduit monitor Description=Virtual filesystem service - Apple File Conduit monitor
PartOf=graphical-session.target
[Service] [Service]
ExecStart=@libexecdir@/gvfs-afc-volume-monitor ExecStart=@libexecdir@/gvfs-afc-volume-monitor
Type=dbus Type=dbus
BusName=org.gtk.vfs.AfcVolumeMonitor BusName=org.gtk.vfs.AfcVolumeMonitor
Slice=session.slice

View File

@ -99,8 +99,6 @@ account_attention_needed_cb (GObject *_object, GParamSpec *pspec, gpointer user_
g_clear_object (&self->mount); g_clear_object (&self->mount);
} }
} }
else
g_volume_mount (G_VOLUME (self), G_MOUNT_MOUNT_NONE, NULL, NULL, NULL, NULL);
} }
/* ---------------------------------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------------------------------- */

View File

@ -326,7 +326,7 @@ get_goa_client_sync (GError **error)
{ {
static GoaClient *client = NULL; static GoaClient *client = NULL;
static GError *_error = NULL; static GError *_error = NULL;
static volatile gsize initialized = 0; static gsize initialized = 0;
if (g_once_init_enter (&initialized)) if (g_once_init_enter (&initialized))
{ {

View File

@ -1,7 +1,9 @@
[Unit] [Unit]
Description=Virtual filesystem service - GNOME Online Accounts monitor Description=Virtual filesystem service - GNOME Online Accounts monitor
PartOf=graphical-session.target
[Service] [Service]
ExecStart=@libexecdir@/gvfs-goa-volume-monitor ExecStart=@libexecdir@/gvfs-goa-volume-monitor
Type=dbus Type=dbus
BusName=org.gtk.vfs.GoaVolumeMonitor BusName=org.gtk.vfs.GoaVolumeMonitor
Slice=session.slice

View File

@ -1,7 +1,9 @@
[Unit] [Unit]
Description=Virtual filesystem service - digital camera monitor Description=Virtual filesystem service - digital camera monitor
PartOf=graphical-session.target
[Service] [Service]
ExecStart=@libexecdir@/gvfs-gphoto2-volume-monitor ExecStart=@libexecdir@/gvfs-gphoto2-volume-monitor
Type=dbus Type=dbus
BusName=org.gtk.vfs.GPhoto2VolumeMonitor BusName=org.gtk.vfs.GPhoto2VolumeMonitor
Slice=session.slice

View File

@ -1,33 +1,33 @@
subdir('proxy') subdir('proxy')
# [[service name suffix, install monitor test data]] # [[service name suffix, install monitor test data]]
monitors = [] monitors = {}
if enable_afc if enable_afc
monitors += [['Afc', true]] monitors += {'Afc': true}
endif endif
if enable_goa if enable_goa
monitors += [['Goa', false]] monitors += {'Goa': false}
endif endif
if enable_gphoto2 if enable_gphoto2
monitors += [['GPhoto2', true]] monitors += {'GPhoto2': true}
endif endif
if enable_mtp if enable_mtp
monitors += [['MTP', false]] monitors += {'MTP': false}
endif endif
if enable_udisks2 if enable_udisks2
monitors += [['UDisks2', true]] monitors += {'UDisks2': true}
endif endif
monitors_test_data = [] monitors_test_data = []
foreach monitor: monitors foreach monitor, monitor_test: monitors
monitor_name = monitor[0].to_lower() monitor_name = monitor.to_lower()
dbus_service = '@0@.@1@VolumeMonitor'.format(gvfs_namespace, monitor[0]) dbus_service = '@0@.@1@VolumeMonitor'.format(gvfs_namespace, monitor)
dbus_exec = 'gvfs-@0@-volume-monitor'.format(monitor_name) dbus_exec = 'gvfs-@0@-volume-monitor'.format(monitor_name)
dbus_systemd_service = '' dbus_systemd_service = ''
@ -66,7 +66,7 @@ foreach monitor: monitors
install_dir: dbus_session_bus_services_dir, install_dir: dbus_session_bus_services_dir,
) )
if monitor[1] if monitor_test
monitors_test_data += [monitor_data, monitor_service] monitors_test_data += [monitor_data, monitor_service]
endif endif

View File

@ -1,7 +1,9 @@
[Unit] [Unit]
Description=Virtual filesystem service - Media Transfer Protocol monitor Description=Virtual filesystem service - Media Transfer Protocol monitor
PartOf=graphical-session.target
[Service] [Service]
ExecStart=@libexecdir@/gvfs-mtp-volume-monitor ExecStart=@libexecdir@/gvfs-mtp-volume-monitor
Type=dbus Type=dbus
BusName=org.gtk.vfs.MTPVolumeMonitor BusName=org.gtk.vfs.MTPVolumeMonitor
Slice=session.slice

View File

@ -1,7 +1,9 @@
[Unit] [Unit]
Description=Virtual filesystem service - disk device monitor Description=Virtual filesystem service - disk device monitor
PartOf=graphical-session.target
[Service] [Service]
ExecStart=@libexecdir@/gvfs-udisks2-volume-monitor ExecStart=@libexecdir@/gvfs-udisks2-volume-monitor
Type=dbus Type=dbus
BusName=org.gtk.vfs.UDisks2VolumeMonitor BusName=org.gtk.vfs.UDisks2VolumeMonitor
Slice=session.slice

View File

@ -580,7 +580,6 @@ typedef struct {
GDrive *drive; GDrive *drive;
GMountOperation *op; GMountOperation *op;
gboolean op_aborted;
gboolean show_processes_up; gboolean show_processes_up;
guint unmount_timer_id; guint unmount_timer_id;
@ -687,13 +686,6 @@ unmount_notify_op_show_processes (UnmountNotifyData *data)
data->show_processes_up = TRUE; data->show_processes_up = TRUE;
} }
static void
unmount_notify_op_aborted (UnmountNotifyData *data)
{
unmount_notify_stop_timer (data);
data->op_aborted = TRUE;
}
static void static void
unmount_notify_op_reply (UnmountNotifyData *data, unmount_notify_op_reply (UnmountNotifyData *data,
GMountOperationResult result) GMountOperationResult result)
@ -704,7 +696,7 @@ unmount_notify_op_reply (UnmountNotifyData *data,
if ((result == G_MOUNT_OPERATION_HANDLED && data->show_processes_up && choice == 1) || if ((result == G_MOUNT_OPERATION_HANDLED && data->show_processes_up && choice == 1) ||
result == G_MOUNT_OPERATION_ABORTED) result == G_MOUNT_OPERATION_ABORTED)
unmount_notify_op_aborted (data); unmount_notify_stop_timer (data);
else if (result == G_MOUNT_OPERATION_HANDLED) else if (result == G_MOUNT_OPERATION_HANDLED)
unmount_notify_ensure_timer (data); unmount_notify_ensure_timer (data);
@ -748,7 +740,7 @@ unmount_notify_data_for_operation (GMountOperation *op,
unmount_notify_data_free); unmount_notify_data_free);
g_signal_connect_swapped (data->op, "aborted", g_signal_connect_swapped (data->op, "aborted",
G_CALLBACK (unmount_notify_op_aborted), data); G_CALLBACK (unmount_notify_stop_timer), data);
g_signal_connect_swapped (data->op, "show-processes", g_signal_connect_swapped (data->op, "show-processes",
G_CALLBACK (unmount_notify_op_show_processes), data); G_CALLBACK (unmount_notify_op_show_processes), data);
g_signal_connect_swapped (data->op, "reply", g_signal_connect_swapped (data->op, "reply",
@ -780,7 +772,7 @@ gvfs_udisks2_unmount_notify_stop (GMountOperation *op,
unmount_notify_stop_timer (data); unmount_notify_stop_timer (data);
if (data->op_aborted || unmount_failed) if (unmount_failed)
return; return;
name = unmount_notify_get_name (data); name = unmount_notify_get_name (data);

View File

@ -1496,13 +1496,14 @@ do_unlock (GTask *task)
task); task);
if (g_strcmp0 (type, "crypto_unknown") == 0) if (g_strcmp0 (type, "crypto_unknown") == 0)
/* Translators: %s is the description of the volume that is being unlocked */ /* Translators: %s is the description of the volume that is being unlocked */
message = g_strdup_printf (_("Enter a passphrase to unlock the volume\n" message = g_strdup_printf (_("Authentication Required\n"
"The volume %s might be a VeraCrypt volume as it contains random data."), "A passphrase is needed to access encrypted data on “%s”.\n"
"The volume might be a VeraCrypt volume as it contains random data."),
data->desc_of_encrypted_to_unlock); data->desc_of_encrypted_to_unlock);
else else
/* Translators: %s is the description of the volume that is being unlocked */ /* Translators: %s is the description of the volume that is being unlocked */
message = g_strdup_printf (_("Enter a passphrase to unlock the volume\n" message = g_strdup_printf (_("Authentication Required\n"
"The passphrase is needed to access encrypted data on %s."), "A passphrase is needed to access encrypted data on %s."),
data->desc_of_encrypted_to_unlock); data->desc_of_encrypted_to_unlock);
pw_ask_flags = G_ASK_PASSWORD_NEED_PASSWORD | G_ASK_PASSWORD_SAVING_SUPPORTED; pw_ask_flags = G_ASK_PASSWORD_NEED_PASSWORD | G_ASK_PASSWORD_SAVING_SUPPORTED;

View File

@ -437,7 +437,7 @@ get_udisks_client_sync (GError **error)
{ {
static UDisksClient *_client = NULL; static UDisksClient *_client = NULL;
static GError *_error = NULL; static GError *_error = NULL;
static volatile gsize initialized = 0; static gsize initialized = 0;
if (g_once_init_enter (&initialized)) if (g_once_init_enter (&initialized))
{ {
@ -606,37 +606,6 @@ update_all (GVfsUDisks2VolumeMonitor *monitor,
/* ---------------------------------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------------------------------- */
static GUnixMountPoint *
get_mount_point_for_mount (GUnixMountEntry *mount_entry)
{
GUnixMountPoint *ret = NULL;
GList *mount_points, *l;
mount_points = g_unix_mount_points_get (NULL);
for (l = mount_points; l != NULL; l = l->next)
{
GUnixMountPoint *mount_point = l->data;
if (g_strcmp0 (g_unix_mount_get_mount_path (mount_entry),
g_unix_mount_point_get_mount_path (mount_point)) == 0)
{
ret = mount_point;
goto out;
}
}
out:
for (l = mount_points; l != NULL; l = l->next)
{
GUnixMountPoint *mount_point = l->data;
if (G_LIKELY (mount_point != ret))
g_unix_mount_point_free (mount_point);
}
g_list_free (mount_points);
return ret;
}
/* ---------------------------------------------------------------------------------------------------- */
static gboolean static gboolean
should_include (const gchar *mount_path, should_include (const gchar *mount_path,
const gchar *options) const gchar *options)
@ -739,7 +708,7 @@ should_include_mount (GVfsUDisks2VolumeMonitor *monitor,
* in prior to g_unix_mount_get_options to keep support of "comment=" options, * in prior to g_unix_mount_get_options to keep support of "comment=" options,
* see https://gitlab.gnome.org/GNOME/gvfs/issues/348. * see https://gitlab.gnome.org/GNOME/gvfs/issues/348.
*/ */
mount_point = get_mount_point_for_mount (mount_entry); mount_point = g_unix_mount_point_at (g_unix_mount_get_mount_path (mount_entry), NULL);
if (mount_point != NULL) if (mount_point != NULL)
{ {
ret = should_include_mount_point (monitor, mount_point); ret = should_include_mount_point (monitor, mount_point);

View File

@ -1,4 +1,5 @@
# please keep this list sorted alphabetically # please keep this list sorted alphabetically
ab
af af
ar ar
as as
@ -33,8 +34,10 @@ hr
hu hu
hi hi
id id
ie
it it
ja ja
ka
kk kk
kn kn
ko ko

2213
po/ab.po Normal file

File diff suppressed because it is too large Load Diff

2500
po/be.po

File diff suppressed because it is too large Load Diff

3133
po/bg.po

File diff suppressed because it is too large Load Diff

886
po/ca.po

File diff suppressed because it is too large Load Diff

879
po/cs.po

File diff suppressed because it is too large Load Diff

1791
po/da.po

File diff suppressed because it is too large Load Diff

910
po/de.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

905
po/es.po

File diff suppressed because it is too large Load Diff

968
po/eu.po

File diff suppressed because it is too large Load Diff

4954
po/fa.po

File diff suppressed because it is too large Load Diff

1044
po/fi.po

File diff suppressed because it is too large Load Diff

969
po/fr.po

File diff suppressed because it is too large Load Diff

982
po/fur.po

File diff suppressed because it is too large Load Diff

912
po/gl.po

File diff suppressed because it is too large Load Diff

1887
po/he.po

File diff suppressed because it is too large Load Diff

890
po/hr.po

File diff suppressed because it is too large Load Diff

946
po/hu.po

File diff suppressed because it is too large Load Diff

944
po/id.po

File diff suppressed because it is too large Load Diff

3420
po/ie.po Normal file

File diff suppressed because it is too large Load Diff

879
po/it.po

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More