1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright (C) 2006-2007 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  * Author: Alexander Larsson <alexl@redhat.com>
21  */
22 
23 #include <config.h>
24 
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/un.h>
30 
31 #include <glib.h>
32 #include <glib/gi18n.h>
33 #include "gvfsbackend.h"
34 #include "gvfsjobsource.h"
35 #include <gvfsjobopenforread.h>
36 #include <gvfsjobopeniconforread.h>
37 #include <gvfsjobopenforwrite.h>
38 #include <gvfsjobqueryinfo.h>
39 #include <gvfsjobqueryfsinfo.h>
40 #include <gvfsjobsetdisplayname.h>
41 #include <gvfsjobenumerate.h>
42 #include <gvfsjobdelete.h>
43 #include <gvfsjobtrash.h>
44 #include <gvfsjobunmount.h>
45 #include <gvfsjobmountmountable.h>
46 #include <gvfsjobunmountmountable.h>
47 #include <gvfsjobstartmountable.h>
48 #include <gvfsjobstopmountable.h>
49 #include <gvfsjobpollmountable.h>
50 #include <gvfsjobmakedirectory.h>
51 #include <gvfsjobmakesymlink.h>
52 #include <gvfsjobcreatemonitor.h>
53 #include <gvfsjobcopy.h>
54 #include <gvfsjobmove.h>
55 #include <gvfsjobpush.h>
56 #include <gvfsjobpull.h>
57 #include <gvfsjobsetattribute.h>
58 #include <gvfsjobqueryattributes.h>
59 #include <gvfsdbus.h>
60 
61 enum {
62   PROP_0,
63   PROP_OBJECT_PATH,
64   PROP_DAEMON
65 };
66 
67 struct _GVfsBackendPrivate
68 {
69   GVfsDaemon *daemon;
70   char *object_path;
71 
72   gboolean is_mounted;
73   char *display_name;
74   char *stable_name;
75   char **x_content_types;
76   GIcon *icon;
77   GIcon *symbolic_icon;
78   char *prefered_filename_encoding;
79   gboolean user_visible;
80   char *default_location;
81   GMountSpec *mount_spec;
82   gboolean block_requests;
83 
84   GSettings *lockdown_settings;
85   gboolean readonly_lockdown;
86 };
87 
88 
89 /* TODO: Real P_() */
90 #define P_(_x) (_x)
91 
92 static void              g_vfs_backend_job_source_iface_init (GVfsJobSourceIface    *iface);
93 static void              g_vfs_backend_get_property          (GObject               *object,
94 							      guint                  prop_id,
95 							      GValue                *value,
96 							      GParamSpec            *pspec);
97 static void              g_vfs_backend_set_property          (GObject               *object,
98 							      guint                  prop_id,
99 							      const GValue          *value,
100 							      GParamSpec            *pspec);
101 static GObject*          g_vfs_backend_constructor           (GType                  type,
102 							      guint                  n_construct_properties,
103 							      GObjectConstructParam *construct_params);
104 
105 
106 G_DEFINE_TYPE_WITH_CODE (GVfsBackend, g_vfs_backend, G_TYPE_OBJECT,
107                          G_ADD_PRIVATE (GVfsBackend)
108                          G_IMPLEMENT_INTERFACE (G_VFS_TYPE_JOB_SOURCE,
109                                                 g_vfs_backend_job_source_iface_init))
110 
111 
112 static GHashTable *registered_backends = NULL;
113 
114 void
g_vfs_register_backend(GType backend_type,const char * type)115 g_vfs_register_backend (GType backend_type,
116 			const char *type)
117 {
118   if (registered_backends == NULL)
119     registered_backends = g_hash_table_new_full (g_str_hash, g_str_equal,
120 						 g_free, NULL);
121 
122   g_hash_table_insert (registered_backends,
123 		       g_strdup (type), (void *)backend_type);
124 }
125 
126 GType
g_vfs_lookup_backend(const char * type)127 g_vfs_lookup_backend (const char *type)
128 {
129   gpointer res;
130 
131   if (registered_backends != NULL)
132     {
133       res = g_hash_table_lookup (registered_backends, type);
134       if (res != NULL)
135 	return (GType)res;
136     }
137 
138   return G_TYPE_INVALID;
139 }
140 
141 static void
g_vfs_backend_finalize(GObject * object)142 g_vfs_backend_finalize (GObject *object)
143 {
144   GVfsBackend *backend;
145 
146   backend = G_VFS_BACKEND (object);
147 
148   g_vfs_daemon_unregister_path (backend->priv->daemon, backend->priv->object_path);
149   g_object_unref (backend->priv->daemon);
150   g_free (backend->priv->object_path);
151 
152   g_free (backend->priv->display_name);
153   g_free (backend->priv->stable_name);
154   g_strfreev (backend->priv->x_content_types);
155   g_clear_object (&backend->priv->icon);
156   g_clear_object (&backend->priv->symbolic_icon);
157   g_free (backend->priv->prefered_filename_encoding);
158   g_free (backend->priv->default_location);
159   if (backend->priv->mount_spec)
160     g_mount_spec_unref (backend->priv->mount_spec);
161 
162   g_clear_object (&backend->priv->lockdown_settings);
163 
164   if (G_OBJECT_CLASS (g_vfs_backend_parent_class)->finalize)
165     (*G_OBJECT_CLASS (g_vfs_backend_parent_class)->finalize) (object);
166 }
167 
168 static void
g_vfs_backend_job_source_iface_init(GVfsJobSourceIface * iface)169 g_vfs_backend_job_source_iface_init (GVfsJobSourceIface *iface)
170 {
171 }
172 
173 static void
g_vfs_backend_class_init(GVfsBackendClass * klass)174 g_vfs_backend_class_init (GVfsBackendClass *klass)
175 {
176   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
177 
178   gobject_class->constructor = g_vfs_backend_constructor;
179   gobject_class->finalize = g_vfs_backend_finalize;
180   gobject_class->set_property = g_vfs_backend_set_property;
181   gobject_class->get_property = g_vfs_backend_get_property;
182 
183   g_object_class_install_property (gobject_class,
184 				   PROP_OBJECT_PATH,
185 				   g_param_spec_string ("object-path",
186 							P_("Backend object path"),
187 							P_("The dbus object path for the backend object."),
188 							"",
189 							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
190 							G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
191   g_object_class_install_property (gobject_class,
192 				   PROP_DAEMON,
193 				   g_param_spec_object ("daemon",
194 							P_("Daemon"),
195 							P_("The daemon this backend is handled by."),
196 							G_VFS_TYPE_DAEMON,
197 							G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
198 							G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
199 }
200 
201 static void
g_vfs_backend_init(GVfsBackend * backend)202 g_vfs_backend_init (GVfsBackend *backend)
203 {
204   backend->priv = g_vfs_backend_get_instance_private (backend);
205   backend->priv->icon = NULL;
206   backend->priv->symbolic_icon = NULL;
207   backend->priv->prefered_filename_encoding = g_strdup ("");
208   backend->priv->display_name = g_strdup ("");
209   backend->priv->stable_name = g_strdup ("");
210   backend->priv->user_visible = TRUE;
211   backend->priv->default_location = g_strdup ("");
212 }
213 
214 static void
g_vfs_backend_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)215 g_vfs_backend_set_property (GObject         *object,
216 			    guint            prop_id,
217 			    const GValue    *value,
218 			    GParamSpec      *pspec)
219 {
220   GVfsBackend *backend = G_VFS_BACKEND (object);
221 
222   switch (prop_id)
223     {
224     case PROP_OBJECT_PATH:
225       backend->priv->object_path = g_value_dup_string (value);
226       break;
227     case PROP_DAEMON:
228       backend->priv->daemon = G_VFS_DAEMON (g_value_dup_object (value));
229       break;
230     default:
231       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
232       break;
233     }
234 }
235 
236 static void
g_vfs_backend_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)237 g_vfs_backend_get_property (GObject    *object,
238 			    guint       prop_id,
239 			    GValue     *value,
240 			    GParamSpec *pspec)
241 {
242   GVfsBackend *backend = G_VFS_BACKEND (object);
243 
244   switch (prop_id)
245     {
246     case PROP_OBJECT_PATH:
247       g_value_set_string (value, backend->priv->object_path);
248       break;
249     case PROP_DAEMON:
250       g_value_set_object (value, backend->priv->daemon);
251       break;
252     default:
253       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
254       break;
255     }
256 }
257 
258 static GDBusInterfaceSkeleton *
register_path_cb(GDBusConnection * conn,const char * obj_path,gpointer data)259 register_path_cb (GDBusConnection *conn,
260                   const char *obj_path,
261                   gpointer data)
262 {
263   GError *error;
264   GVfsDBusMount *skeleton;
265 
266   skeleton = gvfs_dbus_mount_skeleton_new ();
267   g_signal_connect (skeleton, "handle-enumerate", G_CALLBACK (g_vfs_job_enumerate_new_handle), data);
268   g_signal_connect (skeleton, "handle-query-info", G_CALLBACK (g_vfs_job_query_info_new_handle), data);
269   g_signal_connect (skeleton, "handle-query-filesystem-info", G_CALLBACK (g_vfs_job_query_fs_info_new_handle), data);
270   g_signal_connect (skeleton, "handle-set-display-name", G_CALLBACK (g_vfs_job_set_display_name_new_handle), data);
271   g_signal_connect (skeleton, "handle-delete", G_CALLBACK (g_vfs_job_delete_new_handle), data);
272   g_signal_connect (skeleton, "handle-trash", G_CALLBACK (g_vfs_job_trash_new_handle), data);
273   g_signal_connect (skeleton, "handle-make-directory", G_CALLBACK (g_vfs_job_make_directory_new_handle), data);
274   g_signal_connect (skeleton, "handle-make-symbolic-link", G_CALLBACK (g_vfs_job_make_symlink_new_handle), data);
275   g_signal_connect (skeleton, "handle-query-settable-attributes", G_CALLBACK (g_vfs_job_query_settable_attributes_new_handle), data);
276   g_signal_connect (skeleton, "handle-query-writable-namespaces", G_CALLBACK (g_vfs_job_query_writable_namespaces_new_handle), data);
277   g_signal_connect (skeleton, "handle-set-attribute", G_CALLBACK (g_vfs_job_set_attribute_new_handle), data);
278   g_signal_connect (skeleton, "handle-poll-mountable", G_CALLBACK (g_vfs_job_poll_mountable_new_handle), data);
279   g_signal_connect (skeleton, "handle-start-mountable", G_CALLBACK (g_vfs_job_start_mountable_new_handle), data);
280   g_signal_connect (skeleton, "handle-stop-mountable", G_CALLBACK (g_vfs_job_stop_mountable_new_handle), data);
281   g_signal_connect (skeleton, "handle-unmount-mountable", G_CALLBACK (g_vfs_job_unmount_mountable_new_handle), data);
282   g_signal_connect (skeleton, "handle-eject-mountable", G_CALLBACK (g_vfs_job_eject_mountable_new_handle), data);
283   g_signal_connect (skeleton, "handle-mount-mountable", G_CALLBACK (g_vfs_job_mount_mountable_new_handle), data);
284   g_signal_connect (skeleton, "handle-unmount", G_CALLBACK (g_vfs_job_unmount_new_handle), data);
285   g_signal_connect (skeleton, "handle-open-for-read", G_CALLBACK (g_vfs_job_open_for_read_new_handle), data);
286   g_signal_connect (skeleton, "handle-open-for-write", G_CALLBACK (g_vfs_job_open_for_write_new_handle), data);
287   g_signal_connect (skeleton, "handle-open-for-write-flags", G_CALLBACK (g_vfs_job_open_for_write_new_handle_with_flags), data);
288   g_signal_connect (skeleton, "handle-copy", G_CALLBACK (g_vfs_job_copy_new_handle), data);
289   g_signal_connect (skeleton, "handle-move", G_CALLBACK (g_vfs_job_move_new_handle), data);
290   g_signal_connect (skeleton, "handle-push", G_CALLBACK (g_vfs_job_push_new_handle), data);
291   g_signal_connect (skeleton, "handle-pull", G_CALLBACK (g_vfs_job_pull_new_handle), data);
292   g_signal_connect (skeleton, "handle-create-directory-monitor", G_CALLBACK (g_vfs_job_create_directory_monitor_new_handle), data);
293   g_signal_connect (skeleton, "handle-create-file-monitor", G_CALLBACK (g_vfs_job_create_file_monitor_new_handle), data);
294   g_signal_connect (skeleton, "handle-open-icon-for-read", G_CALLBACK (g_vfs_job_open_icon_for_read_new_handle), data);
295 
296   error = NULL;
297   if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (skeleton),
298                                          conn,
299                                          obj_path,
300                                          &error))
301     {
302       g_warning ("Error registering path: %s (%s, %d)\n",
303                   error->message, g_quark_to_string (error->domain), error->code);
304       g_error_free (error);
305     }
306 
307   return G_DBUS_INTERFACE_SKELETON (skeleton);
308 }
309 
310 static GObject*
g_vfs_backend_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_params)311 g_vfs_backend_constructor (GType                  type,
312 			   guint                  n_construct_properties,
313 			   GObjectConstructParam *construct_params)
314 {
315   GObject *object;
316   GVfsBackend *backend;
317 
318   object = (* G_OBJECT_CLASS (g_vfs_backend_parent_class)->constructor) (type,
319 									 n_construct_properties,
320 									 construct_params);
321   backend = G_VFS_BACKEND (object);
322 
323   g_vfs_daemon_register_path (backend->priv->daemon,
324 			      backend->priv->object_path,
325 			      register_path_cb,
326 			      backend);
327 
328   return object;
329 }
330 
331 GVfsDaemon *
g_vfs_backend_get_daemon(GVfsBackend * backend)332 g_vfs_backend_get_daemon (GVfsBackend *backend)
333 {
334   return backend->priv->daemon;
335 }
336 
337 gboolean
g_vfs_backend_is_mounted(GVfsBackend * backend)338 g_vfs_backend_is_mounted (GVfsBackend *backend)
339 {
340   return backend->priv->is_mounted;
341 }
342 
343 void
g_vfs_backend_set_display_name(GVfsBackend * backend,const char * display_name)344 g_vfs_backend_set_display_name (GVfsBackend *backend,
345 				const char *display_name)
346 {
347   g_free (backend->priv->display_name);
348   backend->priv->display_name = g_strdup (display_name);
349 }
350 
351 /**
352  * g_vfs_backend_set_stable_name:
353  * @backend: backend
354  * @stable_name: the stable name
355  *
356  * For filesystems that can change the name during the lifetime
357  * of the filesystem this can be uses to set a separate stable
358  * name. This is used for instance as the directory representing
359  * the mounted file system in the standard UNIX file system
360  * namespace.
361  *
362  * If this function isn't called, the value passed to
363  * g_vfs_backend_set_display_name() will be used instead.
364  **/
365 void
g_vfs_backend_set_stable_name(GVfsBackend * backend,const char * stable_name)366 g_vfs_backend_set_stable_name (GVfsBackend        *backend,
367 			       const char         *stable_name)
368 {
369   g_free (backend->priv->stable_name);
370   backend->priv->stable_name = g_strdup (stable_name);
371 }
372 
373 /**
374  * g_vfs_backend_set_x_content_types:
375  * @backend: backend
376  * @x_content_types: the x-content types
377  *
378  * For backends where the x-content type is known ahead of time and
379  * won't change (such as a CDDA audio disc backend), this function
380  * should be called when the backend is constructed with the given
381  * types.
382  *
383  * See the <ulink url="http://www.freedesktop.org/wiki/Specifications/shared-mime-info-spec">shared-mime-info</ulink>
384  * specification for more on x-content types.
385  **/
386 void
g_vfs_backend_set_x_content_types(GVfsBackend * backend,char ** x_content_types)387 g_vfs_backend_set_x_content_types (GVfsBackend        *backend,
388                                    char              **x_content_types)
389 {
390   g_strfreev (backend->priv->x_content_types);
391   backend->priv->x_content_types = g_strdupv (x_content_types);
392 }
393 
394 void
g_vfs_backend_set_icon_name(GVfsBackend * backend,const char * icon_name)395 g_vfs_backend_set_icon_name (GVfsBackend *backend,
396 			     const char *icon_name)
397 {
398   g_clear_object (&backend->priv->icon);
399   backend->priv->icon = g_themed_icon_new_with_default_fallbacks (icon_name);
400 }
401 
402 void
g_vfs_backend_set_icon(GVfsBackend * backend,GIcon * icon)403 g_vfs_backend_set_icon (GVfsBackend *backend,
404                         GIcon       *icon)
405 {
406   g_clear_object (&backend->priv->icon);
407   backend->priv->icon = g_object_ref (icon);
408 }
409 
410 void
g_vfs_backend_set_symbolic_icon_name(GVfsBackend * backend,const char * icon_name)411 g_vfs_backend_set_symbolic_icon_name (GVfsBackend *backend,
412                                       const char *icon_name)
413 {
414   g_clear_object (&backend->priv->symbolic_icon);
415   backend->priv->symbolic_icon = g_themed_icon_new_with_default_fallbacks (icon_name);
416 }
417 
418 void
g_vfs_backend_set_symbolic_icon(GVfsBackend * backend,GIcon * icon)419 g_vfs_backend_set_symbolic_icon (GVfsBackend *backend,
420                                  GIcon       *icon)
421 {
422   g_clear_object (&backend->priv->symbolic_icon);
423   backend->priv->symbolic_icon = g_object_ref (icon);
424 }
425 
426 void
g_vfs_backend_set_prefered_filename_encoding(GVfsBackend * backend,const char * prefered_filename_encoding)427 g_vfs_backend_set_prefered_filename_encoding (GVfsBackend  *backend,
428 					      const char *prefered_filename_encoding)
429 {
430   g_free (backend->priv->prefered_filename_encoding);
431   backend->priv->prefered_filename_encoding = g_strdup (prefered_filename_encoding);
432 }
433 
434 void
g_vfs_backend_set_user_visible(GVfsBackend * backend,gboolean user_visible)435 g_vfs_backend_set_user_visible (GVfsBackend  *backend,
436 				gboolean user_visible)
437 {
438   backend->priv->user_visible = user_visible;
439 }
440 
441 /**
442  * g_vfs_backend_set_default_location:
443  * @backend: backend
444  * @location: the default location
445  *
446  * With this function the backend can set a "default location", which is a path
447  * that reflects the main entry point for the user (e.g.  * the home directory,
448  * or the root of the volume).
449  *
450  * NB: Does not include the mount prefix, you need to prepend that if there is
451  * one.
452  **/
453 void
g_vfs_backend_set_default_location(GVfsBackend * backend,const char * location)454 g_vfs_backend_set_default_location (GVfsBackend  *backend,
455                                     const char   *location)
456 {
457   g_free (backend->priv->default_location);
458   backend->priv->default_location = g_strdup (location);
459 }
460 
461 void
g_vfs_backend_set_mount_spec(GVfsBackend * backend,GMountSpec * mount_spec)462 g_vfs_backend_set_mount_spec (GVfsBackend *backend,
463 			      GMountSpec *mount_spec)
464 {
465   if (backend->priv->mount_spec)
466     g_mount_spec_unref (backend->priv->mount_spec);
467   backend->priv->mount_spec = g_mount_spec_ref (mount_spec);
468 }
469 
470 const char *
g_vfs_backend_get_backend_type(GVfsBackend * backend)471 g_vfs_backend_get_backend_type (GVfsBackend *backend)
472 {
473   if (backend->priv->mount_spec)
474     return g_mount_spec_get_type (backend->priv->mount_spec);
475   return NULL;
476 }
477 
478 const char *
g_vfs_backend_get_display_name(GVfsBackend * backend)479 g_vfs_backend_get_display_name (GVfsBackend *backend)
480 {
481   return backend->priv->display_name;
482 }
483 
484 const char *
g_vfs_backend_get_stable_name(GVfsBackend * backend)485 g_vfs_backend_get_stable_name (GVfsBackend *backend)
486 {
487   return backend->priv->stable_name;
488 }
489 
490 char **
g_vfs_backend_get_x_content_types(GVfsBackend * backend)491 g_vfs_backend_get_x_content_types (GVfsBackend *backend)
492 {
493   return backend->priv->x_content_types;
494 }
495 
496 GIcon *
g_vfs_backend_get_icon(GVfsBackend * backend)497 g_vfs_backend_get_icon (GVfsBackend *backend)
498 {
499   return backend->priv->icon;
500 }
501 
502 GIcon *
g_vfs_backend_get_symbolic_icon(GVfsBackend * backend)503 g_vfs_backend_get_symbolic_icon (GVfsBackend *backend)
504 {
505   return backend->priv->symbolic_icon;
506 }
507 
508 const char *
g_vfs_backend_get_default_location(GVfsBackend * backend)509 g_vfs_backend_get_default_location (GVfsBackend  *backend)
510 {
511   return backend->priv->default_location;
512 }
513 
514 GMountSpec *
g_vfs_backend_get_mount_spec(GVfsBackend * backend)515 g_vfs_backend_get_mount_spec (GVfsBackend *backend)
516 {
517   return backend->priv->mount_spec;
518 }
519 
520 static void
get_thumbnail_attributes(const char * uri,GFileInfo * info)521 get_thumbnail_attributes (const char *uri,
522                           GFileInfo  *info)
523 {
524   GChecksum *checksum;
525   char *filename;
526   char *basename;
527 
528   checksum = g_checksum_new (G_CHECKSUM_MD5);
529   g_checksum_update (checksum, (const guchar *) uri, strlen (uri));
530 
531   basename = g_strconcat (g_checksum_get_string (checksum), ".png", NULL);
532   g_checksum_free (checksum);
533 
534   filename = g_build_filename (g_get_user_cache_dir (),
535                                "thumbnails", "large", basename,
536                                NULL);
537 
538   if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
539     g_file_info_set_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH, filename);
540   else
541     {
542       g_free (filename);
543       filename = g_build_filename (g_get_user_cache_dir (),
544                                    "thumbnails", "normal", basename,
545                                    NULL);
546 
547       if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
548         g_file_info_set_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH, filename);
549       else
550         {
551           g_free (filename);
552           filename = g_build_filename (g_get_user_cache_dir (),
553                                        "thumbnails", "fail",
554                                        "gnome-thumbnail-factory",
555                                        basename,
556                                        NULL);
557 
558           if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
559             g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED, TRUE);
560         }
561     }
562   g_free (basename);
563   g_free (filename);
564 }
565 
566 void
g_vfs_backend_add_auto_info(GVfsBackend * backend,GFileAttributeMatcher * matcher,GFileInfo * info,const char * uri)567 g_vfs_backend_add_auto_info (GVfsBackend *backend,
568 			     GFileAttributeMatcher *matcher,
569 			     GFileInfo *info,
570 			     const char *uri)
571 {
572   GMountSpec *spec;
573   char *id;
574 
575   if (g_file_attribute_matcher_matches (matcher,
576 					G_FILE_ATTRIBUTE_ID_FILESYSTEM))
577     {
578       spec = g_vfs_backend_get_mount_spec (backend);
579       if (spec)
580 	{
581 	  id = g_mount_spec_to_string (spec);
582 	  g_file_info_set_attribute_string (info,
583 					    G_FILE_ATTRIBUTE_ID_FILESYSTEM,
584 					    id);
585 	  g_free (id);
586 	}
587     }
588 
589   if (uri != NULL &&
590       (g_file_attribute_matcher_matches (matcher,
591                                          G_FILE_ATTRIBUTE_THUMBNAIL_PATH) ||
592        g_file_attribute_matcher_matches (matcher,
593                                          G_FILE_ATTRIBUTE_THUMBNAILING_FAILED)))
594     get_thumbnail_attributes (uri, info);
595 
596   if (backend->priv->readonly_lockdown)
597     {
598       g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE, FALSE);
599       g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME, FALSE);
600       g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, FALSE);
601       g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE, FALSE);
602     }
603 }
604 
605 void
g_vfs_backend_add_auto_fs_info(GVfsBackend * backend,GFileAttributeMatcher * matcher,GFileInfo * info)606 g_vfs_backend_add_auto_fs_info (GVfsBackend *backend,
607                                 GFileAttributeMatcher *matcher,
608                                 GFileInfo *info)
609 {
610   const char *type;
611 
612   type = g_vfs_backend_get_backend_type (backend);
613   if (type)
614     g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_GVFS_BACKEND, type);
615 
616   if (backend->priv->readonly_lockdown)
617     g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY, TRUE);
618 }
619 
620 void
g_vfs_backend_set_block_requests(GVfsBackend * backend,gboolean value)621 g_vfs_backend_set_block_requests (GVfsBackend *backend, gboolean value)
622 {
623   backend->priv->block_requests = value;
624 }
625 
626 gboolean
g_vfs_backend_get_block_requests(GVfsBackend * backend)627 g_vfs_backend_get_block_requests (GVfsBackend *backend)
628 {
629   return backend->priv->block_requests;
630 }
631 
632 gboolean
g_vfs_backend_invocation_first_handler(GVfsDBusMount * object,GDBusMethodInvocation * invocation,GVfsBackend * backend)633 g_vfs_backend_invocation_first_handler (GVfsDBusMount *object,
634                                         GDBusMethodInvocation *invocation,
635                                         GVfsBackend *backend)
636 {
637   GDBusConnection *connection;
638   GCredentials *credentials;
639   pid_t pid = -1;
640 
641   connection = g_dbus_method_invocation_get_connection (invocation);
642   credentials = g_dbus_connection_get_peer_credentials (connection);
643   if (credentials)
644     pid = g_credentials_get_unix_pid (credentials, NULL);
645 
646   g_debug ("backend_dbus_handler %s:%s (pid=%ld)\n",
647            g_dbus_method_invocation_get_interface_name (invocation),
648            g_dbus_method_invocation_get_method_name (invocation),
649            (long)pid);
650 
651   if (backend->priv->block_requests)
652     {
653       g_dbus_method_invocation_return_error (invocation,
654                                              G_IO_ERROR,
655                                              G_IO_ERROR_NOT_MOUNTED,
656                                              "%s", "Backend currently unmounting");
657       return TRUE;
658     }
659 
660   return FALSE;
661 }
662 
663 static void
create_mount_tracker_proxy(GTask * task,GAsyncReadyCallback callback)664 create_mount_tracker_proxy (GTask *task,
665                             GAsyncReadyCallback callback)
666 {
667   gvfs_dbus_mount_tracker_proxy_new_for_bus (G_BUS_TYPE_SESSION,
668                                              G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
669                                              G_VFS_DBUS_DAEMON_NAME,
670                                              G_VFS_DBUS_MOUNTTRACKER_PATH,
671                                              NULL,
672                                              callback,
673                                              task);
674 }
675 
676 static void
register_mount_cb(GVfsDBusMountTracker * proxy,GAsyncResult * res,gpointer user_data)677 register_mount_cb (GVfsDBusMountTracker *proxy,
678                    GAsyncResult *res,
679                    gpointer user_data)
680 {
681   GTask *task = G_TASK (user_data);
682   GError *error = NULL;
683 
684   if (!gvfs_dbus_mount_tracker_call_register_mount_finish (proxy, res, &error))
685     {
686       g_dbus_error_strip_remote_error (error);
687       g_task_return_error (task, error);
688     }
689   else
690     {
691       g_task_return_boolean (task, TRUE);
692     }
693 
694   g_object_unref (task);
695 }
696 
697 static void
register_mount_got_proxy_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)698 register_mount_got_proxy_cb (GObject *source_object,
699                              GAsyncResult *res,
700                              gpointer user_data)
701 {
702   GTask *task = G_TASK (user_data);
703   GVfsDBusMountTracker *proxy;
704   GError *error = NULL;
705   GVfsBackend *backend = G_VFS_BACKEND (g_task_get_source_object (task));
706   char *stable_name;
707   char *x_content_types_string;
708   char *icon_str;
709   char *symbolic_icon_str;
710 
711   proxy = gvfs_dbus_mount_tracker_proxy_new_for_bus_finish (res, &error);
712   if (proxy == NULL)
713     {
714       g_dbus_error_strip_remote_error (error);
715       g_task_return_error (task, error);
716       g_object_unref (task);
717       return;
718     }
719 
720   backend->priv->is_mounted = TRUE;
721 
722   if (backend->priv->x_content_types != NULL && g_strv_length (backend->priv->x_content_types) > 0)
723     x_content_types_string = g_strjoinv (" ", backend->priv->x_content_types);
724   else
725     x_content_types_string = g_strdup ("");
726 
727   if (backend->priv->icon != NULL)
728     icon_str = g_icon_to_string (backend->priv->icon);
729   else
730     icon_str = g_strdup ("");
731 
732   if (backend->priv->symbolic_icon != NULL)
733     symbolic_icon_str = g_icon_to_string (backend->priv->symbolic_icon);
734   else
735     symbolic_icon_str = g_strdup ("");
736 
737   stable_name = g_mount_spec_to_string (backend->priv->mount_spec);
738 
739   gvfs_dbus_mount_tracker_call_register_mount (proxy,
740                                                backend->priv->object_path,
741                                                backend->priv->display_name,
742                                                stable_name,
743                                                x_content_types_string,
744                                                icon_str,
745                                                symbolic_icon_str,
746                                                backend->priv->prefered_filename_encoding,
747                                                backend->priv->user_visible,
748                                                g_mount_spec_to_dbus (backend->priv->mount_spec),
749                                                backend->priv->default_location ? backend->priv->default_location : "",
750                                                NULL,
751                                                (GAsyncReadyCallback) register_mount_cb,
752                                                task);
753 
754   g_free (stable_name);
755   g_free (x_content_types_string);
756   g_free (icon_str);
757   g_free (symbolic_icon_str);
758   g_object_unref (proxy);
759 }
760 
761 void
g_vfs_backend_register_mount(GVfsBackend * backend,GAsyncReadyCallback callback,gpointer user_data)762 g_vfs_backend_register_mount (GVfsBackend *backend,
763                               GAsyncReadyCallback callback,
764 			      gpointer user_data)
765 {
766   GTask *task;
767 
768   task = g_task_new (backend, NULL, callback, user_data);
769   g_task_set_source_tag (task, g_vfs_backend_register_mount);
770 
771   create_mount_tracker_proxy (task, register_mount_got_proxy_cb);
772 }
773 
774 gboolean
g_vfs_backend_register_mount_finish(GVfsBackend * backend,GAsyncResult * res,GError ** error)775 g_vfs_backend_register_mount_finish (GVfsBackend *backend,
776                                      GAsyncResult *res,
777                                      GError **error)
778 {
779   g_return_val_if_fail (g_task_is_valid (res, backend), FALSE);
780   g_return_val_if_fail (g_async_result_is_tagged (res, g_vfs_backend_register_mount), FALSE);
781 
782   return g_task_propagate_boolean (G_TASK (res), error);
783 }
784 
785 static void
unregister_mount_cb(GVfsDBusMountTracker * proxy,GAsyncResult * res,gpointer user_data)786 unregister_mount_cb (GVfsDBusMountTracker *proxy,
787                      GAsyncResult *res,
788                      gpointer user_data)
789 {
790   GTask *task = G_TASK (user_data);
791   GError *error = NULL;
792 
793   if (!gvfs_dbus_mount_tracker_call_unregister_mount_finish (proxy, res, &error))
794     {
795       g_dbus_error_strip_remote_error (error);
796       g_task_return_error (task, error);
797     }
798   else
799     {
800       g_task_return_boolean (task, TRUE);
801     }
802 
803   g_object_unref (task);
804 }
805 
806 static void
unregister_mount_got_proxy_cb(GObject * source_object,GAsyncResult * res,gpointer user_data)807 unregister_mount_got_proxy_cb (GObject *source_object,
808                                GAsyncResult *res,
809                                gpointer user_data)
810 {
811   GTask *task = G_TASK (user_data);
812   GVfsDBusMountTracker *proxy;
813   GError *error = NULL;
814   GVfsBackend *backend = G_VFS_BACKEND (g_task_get_source_object (task));
815 
816   proxy = gvfs_dbus_mount_tracker_proxy_new_for_bus_finish (res, &error);
817   if (proxy == NULL)
818     {
819       g_dbus_error_strip_remote_error (error);
820       g_task_return_error (task, error);
821       g_object_unref (task);
822       return;
823     }
824 
825   gvfs_dbus_mount_tracker_call_unregister_mount (proxy,
826                                                  backend->priv->object_path,
827                                                  NULL,
828                                                  (GAsyncReadyCallback) unregister_mount_cb,
829                                                  task);
830 
831   g_object_unref (proxy);
832 }
833 
834 void
g_vfs_backend_unregister_mount(GVfsBackend * backend,GAsyncReadyCallback callback,gpointer user_data)835 g_vfs_backend_unregister_mount (GVfsBackend *backend,
836                                 GAsyncReadyCallback callback,
837 				gpointer user_data)
838 {
839   GTask *task;
840 
841   task = g_task_new (backend, NULL, callback, user_data);
842   g_task_set_source_tag (task, g_vfs_backend_unregister_mount);
843 
844   create_mount_tracker_proxy (task, unregister_mount_got_proxy_cb);
845 }
846 
847 gboolean
g_vfs_backend_unregister_mount_finish(GVfsBackend * backend,GAsyncResult * res,GError ** error)848 g_vfs_backend_unregister_mount_finish (GVfsBackend *backend,
849                                        GAsyncResult *res,
850                                        GError **error)
851 {
852   g_return_val_if_fail (g_task_is_valid (res, backend), FALSE);
853   g_return_val_if_fail (g_async_result_is_tagged (res, g_vfs_backend_unregister_mount), FALSE);
854 
855   return g_task_propagate_boolean (G_TASK (res), error);
856 }
857 
858 /* ------------------------------------------------------------------------------------------------- */
859 
860 typedef struct
861 {
862   GMountSource *mount_source;
863 
864   const gchar *message;
865   const gchar *choices[3];
866 
867   gboolean no_more_processes;
868 
869   guint timeout_id;
870 } UnmountWithOpData;
871 
872 static void
on_show_processes_reply(GMountSource * mount_source,GAsyncResult * res,gpointer user_data)873 on_show_processes_reply (GMountSource  *mount_source,
874                          GAsyncResult  *res,
875                          gpointer       user_data)
876 {
877   GTask *task = G_TASK (user_data);
878   UnmountWithOpData *data = g_task_get_task_data (task);
879   gboolean ret, aborted;
880   gint choice;
881 
882   if (data->timeout_id != 0)
883     g_source_remove (data->timeout_id);
884 
885   ret = g_mount_source_show_processes_finish (mount_source, res, &aborted, &choice);
886   if (!data->no_more_processes && !ret)
887     {
888       /*  If the "show-processes" signal wasn't handled */
889       g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_BUSY,
890                                _("File system is busy"));
891     }
892   else if (!data->no_more_processes && (aborted || choice == 1))
893     {
894       g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED,
895                                "GMountOperation aborted");
896     }
897   else
898     {
899       g_task_return_boolean (task, TRUE);
900     }
901 
902   g_object_unref (task);
903 }
904 
905 static gboolean
on_update_processes_timeout(gpointer user_data)906 on_update_processes_timeout (gpointer user_data)
907 {
908   GTask *task = G_TASK (user_data);
909   UnmountWithOpData *data = g_task_get_task_data (task);
910   GArray *processes;
911   GVfsBackend *backend = G_VFS_BACKEND (g_task_get_source_object (task));
912   GVfsDaemon *daemon = g_vfs_backend_get_daemon (backend);
913 
914   if (!g_vfs_daemon_has_blocking_processes (daemon))
915     {
916       g_mount_source_abort (data->mount_source);
917       data->timeout_id = 0;
918       data->no_more_processes = TRUE;
919 
920       return G_SOURCE_REMOVE;
921     }
922   else
923     {
924       processes = g_vfs_daemon_get_blocking_processes (daemon);
925       g_mount_source_show_processes_async (data->mount_source,
926                                            data->message,
927                                            processes,
928                                            data->choices,
929                                            (GAsyncReadyCallback) on_show_processes_reply,
930                                            task);
931       g_array_unref (processes);
932 
933       return G_SOURCE_CONTINUE;
934     }
935 }
936 
937 static void
unmount_with_op_data_free(UnmountWithOpData * data)938 unmount_with_op_data_free (UnmountWithOpData *data)
939 {
940   g_free (data);
941 }
942 
943 
944 /**
945  * g_vfs_backend_unmount_with_operation_finish:
946  * @backend: A #GVfsBackend.
947  * @res: A #GAsyncResult obtained from the @callback function passed
948  *     to g_vfs_backend_unmount_with_operation().
949  * @error: A #GError, or NULL.
950  *
951  * Gets the result of the operation started by
952  * gvfs_backend_unmount_with_operation_sync().
953  *
954  * If the operation was cancelled, G_IO_ERROR_FAILED_HANDLED will be returned.
955  * If the operation wasn't interacted and there were outstanding jobs,
956  * G_IO_ERROR_BUSY will be returned.
957  *
958  * Returns: %TRUE if the backend should be unmounted (either no blocking
959  *     processes or the user decided to unmount anyway), %FALSE if
960  *     no action should be taken (error is set).
961  */
962 gboolean
g_vfs_backend_unmount_with_operation_finish(GVfsBackend * backend,GAsyncResult * res,GError ** error)963 g_vfs_backend_unmount_with_operation_finish (GVfsBackend *backend,
964                                              GAsyncResult *res,
965                                              GError **error)
966 {
967   g_return_val_if_fail (g_task_is_valid (res, backend), FALSE);
968   g_return_val_if_fail (g_async_result_is_tagged (res, g_vfs_backend_unmount_with_operation), FALSE);
969 
970   return g_task_propagate_boolean (G_TASK (res), error);
971 }
972 
973 /**
974  * gvfs_backend_unmount_with_operation:
975  * @backend: A #GVfsBackend.
976  * @callback: A #GAsyncReadyCallback.
977  * @user_data: User data to pass to @callback.
978  *
979  * Utility function to checks if there are pending operations on
980  * @backend preventing unmount. If not, then @callback is invoked
981  * immediately.
982  *
983  * Otherwise, a dialog will be shown (using @mount_source) to interact
984  * with the user about blocking processes (e.g. using the
985  * #GMountOperation::show-processes signal). The list of blocking
986  * processes is continuously updated.
987  *
988  * Once the user has decided (or if it's not possible to interact with
989  * the user), @callback will be invoked. You can then call
990  * g_vfs_backend_unmount_with_operation_finish() to get the result
991  * of the operation.
992  */
993 void
g_vfs_backend_unmount_with_operation(GVfsBackend * backend,GMountSource * mount_source,GAsyncReadyCallback callback,gpointer user_data)994 g_vfs_backend_unmount_with_operation (GVfsBackend        *backend,
995                                       GMountSource       *mount_source,
996                                       GAsyncReadyCallback callback,
997                                       gpointer            user_data)
998 {
999   GArray *processes;
1000   UnmountWithOpData *data;
1001   GVfsDaemon *daemon;
1002   GTask *task;
1003 
1004   g_return_if_fail (G_VFS_IS_BACKEND (backend));
1005   g_return_if_fail (G_IS_MOUNT_SOURCE (mount_source));
1006   g_return_if_fail (callback != NULL);
1007 
1008   task = g_task_new (backend, NULL, callback, user_data);
1009   g_task_set_source_tag (task, g_vfs_backend_unmount_with_operation);
1010 
1011   daemon = g_vfs_backend_get_daemon (backend);
1012 
1013   /* if no processes are blocking, complete immediately */
1014   if (!g_vfs_daemon_has_blocking_processes (daemon))
1015     {
1016       g_task_return_boolean (task, TRUE);
1017       g_object_unref (task);
1018       return;
1019     }
1020 
1021   data = g_new0 (UnmountWithOpData, 1);
1022   data->mount_source = mount_source;
1023 
1024   data->choices[0] = _("Unmount Anyway");
1025   data->choices[1] = _("Cancel");
1026   data->choices[2] = NULL;
1027   data->message = _("Volume is busy\n"
1028                     "One or more applications are keeping the volume busy.");
1029 
1030   g_task_set_task_data (task, data, (GDestroyNotify) unmount_with_op_data_free);
1031 
1032   /* show processes */
1033   processes = g_vfs_daemon_get_blocking_processes (daemon);
1034   g_mount_source_show_processes_async (mount_source,
1035                                        data->message,
1036                                        processes,
1037                                        data->choices,
1038                                        (GAsyncReadyCallback) on_show_processes_reply,
1039                                        task);
1040   g_array_unref (processes);
1041 
1042   /* update these processes every two secs */
1043   data->timeout_id = g_timeout_add_seconds (2, on_update_processes_timeout, task);
1044 }
1045 
1046 static void
forced_unregister_mount_callback(GVfsBackend * backend,GAsyncResult * res,gpointer user_data)1047 forced_unregister_mount_callback (GVfsBackend *backend,
1048                                   GAsyncResult *res,
1049                                   gpointer user_data)
1050 {
1051   GVfsDaemon *daemon;
1052   GError *error = NULL;
1053 
1054   g_debug ("forced_unregister_mount_callback\n");
1055   if (!g_vfs_backend_unregister_mount_finish (backend, res, &error))
1056     {
1057       g_dbus_error_strip_remote_error (error);
1058       g_warning ("Error unregistering mount: %s (%s, %d)\n",
1059                   error->message, g_quark_to_string (error->domain), error->code);
1060       g_error_free (error);
1061     }
1062 
1063   /* Unlink job source from daemon */
1064   daemon = g_vfs_backend_get_daemon (backend);
1065   g_vfs_daemon_close_active_channels (daemon, backend);
1066   g_vfs_job_source_closed (G_VFS_JOB_SOURCE (backend));
1067 }
1068 
1069 void
g_vfs_backend_force_unmount(GVfsBackend * backend)1070 g_vfs_backend_force_unmount (GVfsBackend *backend)
1071 {
1072   g_vfs_backend_set_block_requests (backend, TRUE);
1073   g_vfs_backend_unregister_mount (backend,
1074                                   (GAsyncReadyCallback) forced_unregister_mount_callback,
1075                                   NULL);
1076 }
1077 
1078 static void
lockdown_settings_changed(GSettings * settings,gchar * key,gpointer user_data)1079 lockdown_settings_changed (GSettings *settings,
1080                            gchar     *key,
1081                            gpointer   user_data)
1082 {
1083   GVfsBackend *backend = G_VFS_BACKEND (user_data);
1084 
1085   backend->priv->readonly_lockdown = g_settings_get_boolean (settings,
1086                                                              "mount-removable-storage-devices-as-read-only");
1087 }
1088 
1089 
1090 void
g_vfs_backend_handle_readonly_lockdown(GVfsBackend * backend)1091 g_vfs_backend_handle_readonly_lockdown (GVfsBackend *backend)
1092 {
1093   backend->priv->lockdown_settings = g_settings_new ("org.gnome.desktop.lockdown");
1094   backend->priv->readonly_lockdown = g_settings_get_boolean (backend->priv->lockdown_settings,
1095                                                              "mount-removable-storage-devices-as-read-only");
1096   g_signal_connect_object (backend->priv->lockdown_settings,
1097                            "changed",
1098                            G_CALLBACK (lockdown_settings_changed),
1099                            backend,
1100                            0);
1101 }
1102 
1103 gboolean
g_vfs_backend_get_readonly_lockdown(GVfsBackend * backend)1104 g_vfs_backend_get_readonly_lockdown (GVfsBackend *backend)
1105 {
1106   return backend->priv->readonly_lockdown;
1107 }
1108