• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 
3 /* GIO - GLib Input, Output and Streaming Library
4  *
5  * Copyright (C) 2006-2007 Red Hat, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General
18  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19  *
20  * Author: Alexander Larsson <alexl@redhat.com>
21  *         David Zeuthen <davidz@redhat.com>
22  */
23 
24 #include "config.h"
25 
26 #include <string.h>
27 #include <sys/wait.h>
28 #include <unistd.h>
29 
30 #include <glib.h>
31 #include "gunixvolume.h"
32 #include "gunixmount.h"
33 #include "gunixmounts.h"
34 #include "gthemedicon.h"
35 #include "gvolume.h"
36 #include "gvolumemonitor.h"
37 #include "gtask.h"
38 #include "gioerror.h"
39 #include "glibintl.h"
40 /* for BUFSIZ */
41 #include <stdio.h>
42 
43 
44 struct _GUnixVolume {
45   GObject parent;
46 
47   GVolumeMonitor *volume_monitor;
48   GUnixMount     *mount; /* owned by volume monitor */
49 
50   char *device_path;
51   char *mount_path;
52   gboolean can_eject;
53 
54   char *identifier;
55   char *identifier_type;
56 
57   char *name;
58   GIcon *icon;
59   GIcon *symbolic_icon;
60 };
61 
62 static void g_unix_volume_volume_iface_init (GVolumeIface *iface);
63 
64 #define g_unix_volume_get_type _g_unix_volume_get_type
G_DEFINE_TYPE_WITH_CODE(GUnixVolume,g_unix_volume,G_TYPE_OBJECT,G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME,g_unix_volume_volume_iface_init))65 G_DEFINE_TYPE_WITH_CODE (GUnixVolume, g_unix_volume, G_TYPE_OBJECT,
66 			 G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME,
67 						g_unix_volume_volume_iface_init))
68 
69 static void
70 g_unix_volume_finalize (GObject *object)
71 {
72   GUnixVolume *volume;
73 
74   volume = G_UNIX_VOLUME (object);
75 
76   if (volume->volume_monitor != NULL)
77     g_object_unref (volume->volume_monitor);
78 
79   if (volume->mount)
80     _g_unix_mount_unset_volume (volume->mount, volume);
81 
82   g_object_unref (volume->icon);
83   g_object_unref (volume->symbolic_icon);
84   g_free (volume->name);
85   g_free (volume->mount_path);
86   g_free (volume->device_path);
87   g_free (volume->identifier);
88   g_free (volume->identifier_type);
89 
90   G_OBJECT_CLASS (g_unix_volume_parent_class)->finalize (object);
91 }
92 
93 static void
g_unix_volume_class_init(GUnixVolumeClass * klass)94 g_unix_volume_class_init (GUnixVolumeClass *klass)
95 {
96   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
97 
98   gobject_class->finalize = g_unix_volume_finalize;
99 }
100 
101 static void
g_unix_volume_init(GUnixVolume * unix_volume)102 g_unix_volume_init (GUnixVolume *unix_volume)
103 {
104 }
105 
106 GUnixVolume *
_g_unix_volume_new(GVolumeMonitor * volume_monitor,GUnixMountPoint * mountpoint)107 _g_unix_volume_new (GVolumeMonitor  *volume_monitor,
108                     GUnixMountPoint *mountpoint)
109 {
110   GUnixVolume *volume;
111 
112   if (!(g_unix_mount_point_is_user_mountable (mountpoint) ||
113 	g_str_has_prefix (g_unix_mount_point_get_device_path (mountpoint), "/vol/")) ||
114       g_unix_mount_point_is_loopback (mountpoint))
115     return NULL;
116 
117   volume = g_object_new (G_TYPE_UNIX_VOLUME, NULL);
118   volume->volume_monitor = volume_monitor != NULL ? g_object_ref (volume_monitor) : NULL;
119   volume->mount_path = g_strdup (g_unix_mount_point_get_mount_path (mountpoint));
120   volume->device_path = g_strdup (g_unix_mount_point_get_device_path (mountpoint));
121   volume->can_eject = g_unix_mount_point_guess_can_eject (mountpoint);
122 
123   volume->name = g_unix_mount_point_guess_name (mountpoint);
124   volume->icon = g_unix_mount_point_guess_icon (mountpoint);
125   volume->symbolic_icon = g_unix_mount_point_guess_symbolic_icon (mountpoint);
126 
127 
128   if (strcmp (g_unix_mount_point_get_fs_type (mountpoint), "nfs") == 0)
129     {
130       volume->identifier_type = g_strdup (G_VOLUME_IDENTIFIER_KIND_NFS_MOUNT);
131       volume->identifier = g_strdup (volume->device_path);
132     }
133   else if (g_str_has_prefix (volume->device_path, "LABEL="))
134     {
135       volume->identifier_type = g_strdup (G_VOLUME_IDENTIFIER_KIND_LABEL);
136       volume->identifier = g_strdup (volume->device_path + 6);
137     }
138   else if (g_str_has_prefix (volume->device_path, "UUID="))
139     {
140       volume->identifier_type = g_strdup (G_VOLUME_IDENTIFIER_KIND_UUID);
141       volume->identifier = g_strdup (volume->device_path + 5);
142     }
143   else if (g_path_is_absolute (volume->device_path))
144     {
145       volume->identifier_type = g_strdup (G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
146       volume->identifier = g_strdup (volume->device_path);
147     }
148 
149   return volume;
150 }
151 
152 void
_g_unix_volume_disconnected(GUnixVolume * volume)153 _g_unix_volume_disconnected (GUnixVolume *volume)
154 {
155   if (volume->mount)
156     {
157       _g_unix_mount_unset_volume (volume->mount, volume);
158       volume->mount = NULL;
159     }
160 }
161 
162 void
_g_unix_volume_set_mount(GUnixVolume * volume,GUnixMount * mount)163 _g_unix_volume_set_mount (GUnixVolume *volume,
164                           GUnixMount  *mount)
165 {
166   if (volume->mount == mount)
167     return;
168 
169   if (volume->mount)
170     _g_unix_mount_unset_volume (volume->mount, volume);
171 
172   volume->mount = mount;
173 
174   /* TODO: Emit changed in idle to avoid locking issues */
175   g_signal_emit_by_name (volume, "changed");
176   if (volume->volume_monitor != NULL)
177     g_signal_emit_by_name (volume->volume_monitor, "volume-changed", volume);
178 }
179 
180 void
_g_unix_volume_unset_mount(GUnixVolume * volume,GUnixMount * mount)181 _g_unix_volume_unset_mount (GUnixVolume  *volume,
182                             GUnixMount *mount)
183 {
184   if (volume->mount == mount)
185     {
186       volume->mount = NULL;
187       /* TODO: Emit changed in idle to avoid locking issues */
188       g_signal_emit_by_name (volume, "changed");
189       if (volume->volume_monitor != NULL)
190         g_signal_emit_by_name (volume->volume_monitor, "volume-changed", volume);
191     }
192 }
193 
194 static GIcon *
g_unix_volume_get_icon(GVolume * volume)195 g_unix_volume_get_icon (GVolume *volume)
196 {
197   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
198   return g_object_ref (unix_volume->icon);
199 }
200 
201 static GIcon *
g_unix_volume_get_symbolic_icon(GVolume * volume)202 g_unix_volume_get_symbolic_icon (GVolume *volume)
203 {
204   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
205   return g_object_ref (unix_volume->symbolic_icon);
206 }
207 
208 static char *
g_unix_volume_get_name(GVolume * volume)209 g_unix_volume_get_name (GVolume *volume)
210 {
211   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
212   return g_strdup (unix_volume->name);
213 }
214 
215 static char *
g_unix_volume_get_uuid(GVolume * volume)216 g_unix_volume_get_uuid (GVolume *volume)
217 {
218   return NULL;
219 }
220 
221 static gboolean
g_unix_volume_can_mount(GVolume * volume)222 g_unix_volume_can_mount (GVolume *volume)
223 {
224   return TRUE;
225 }
226 
227 static gboolean
g_unix_volume_can_eject(GVolume * volume)228 g_unix_volume_can_eject (GVolume *volume)
229 {
230   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
231   return unix_volume->can_eject;
232 }
233 
234 static gboolean
g_unix_volume_should_automount(GVolume * volume)235 g_unix_volume_should_automount (GVolume *volume)
236 {
237   /* We automount all local volumes because we don't even
238    * make the internal stuff visible
239    */
240   return TRUE;
241 }
242 
243 static GDrive *
g_unix_volume_get_drive(GVolume * volume)244 g_unix_volume_get_drive (GVolume *volume)
245 {
246   return NULL;
247 }
248 
249 static GMount *
g_unix_volume_get_mount(GVolume * volume)250 g_unix_volume_get_mount (GVolume *volume)
251 {
252   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
253 
254   if (unix_volume->mount != NULL)
255     return g_object_ref (G_MOUNT (unix_volume->mount));
256 
257   return NULL;
258 }
259 
260 
261 gboolean
_g_unix_volume_has_mount_path(GUnixVolume * volume,const char * mount_path)262 _g_unix_volume_has_mount_path (GUnixVolume *volume,
263                                const char  *mount_path)
264 {
265   return strcmp (volume->mount_path, mount_path) == 0;
266 }
267 
268 static void
eject_mount_done(GObject * source,GAsyncResult * result,gpointer user_data)269 eject_mount_done (GObject      *source,
270                   GAsyncResult *result,
271                   gpointer      user_data)
272 {
273   GSubprocess *subprocess = G_SUBPROCESS (source);
274   GTask *task = user_data;
275   GError *error = NULL;
276   gchar *stderr_str;
277   GUnixVolume *unix_volume;
278 
279   if (!g_subprocess_communicate_utf8_finish (subprocess, result, NULL, &stderr_str, &error))
280     {
281       g_task_return_error (task, error);
282       g_error_free (error);
283     }
284   else /* successful communication */
285     {
286       if (!g_subprocess_get_successful (subprocess))
287         /* ...but bad exit code */
288         g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED, "%s", stderr_str);
289       else
290         {
291           /* ...and successful exit code */
292           unix_volume = G_UNIX_VOLUME (g_task_get_source_object (task));
293           _g_unix_volume_monitor_update (G_UNIX_VOLUME_MONITOR (unix_volume->volume_monitor));
294           g_task_return_boolean (task, TRUE);
295         }
296 
297       g_free (stderr_str);
298     }
299 
300   g_object_unref (task);
301 }
302 
303 static void
eject_mount_do(GVolume * volume,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data,const gchar * const * argv,const gchar * task_name)304 eject_mount_do (GVolume              *volume,
305                 GCancellable         *cancellable,
306                 GAsyncReadyCallback   callback,
307                 gpointer              user_data,
308                 const gchar * const  *argv,
309                 const gchar          *task_name)
310 {
311   GSubprocess *subprocess;
312   GError *error = NULL;
313   GTask *task;
314 
315   task = g_task_new (volume, cancellable, callback, user_data);
316   g_task_set_source_tag (task, eject_mount_do);
317   g_task_set_name (task, task_name);
318 
319   if (g_task_return_error_if_cancelled (task))
320     {
321       g_object_unref (task);
322       return;
323     }
324 
325   subprocess = g_subprocess_newv (argv, G_SUBPROCESS_FLAGS_STDOUT_SILENCE | G_SUBPROCESS_FLAGS_STDERR_PIPE, &error);
326   g_assert_no_error (error);
327 
328   g_subprocess_communicate_utf8_async (subprocess, NULL,
329                                        g_task_get_cancellable (task),
330                                        eject_mount_done, task);
331 }
332 
333 static void
g_unix_volume_mount(GVolume * volume,GMountMountFlags flags,GMountOperation * mount_operation,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)334 g_unix_volume_mount (GVolume            *volume,
335                      GMountMountFlags    flags,
336                      GMountOperation     *mount_operation,
337                      GCancellable        *cancellable,
338                      GAsyncReadyCallback  callback,
339                      gpointer             user_data)
340 {
341   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
342   const gchar *argv[] = { "mount", NULL, NULL };
343 
344   if (unix_volume->mount_path != NULL)
345     argv[1] = unix_volume->mount_path;
346   else
347     argv[1] = unix_volume->device_path;
348 
349   eject_mount_do (volume, cancellable, callback, user_data, argv, "[gio] mount volume");
350 }
351 
352 static gboolean
g_unix_volume_mount_finish(GVolume * volume,GAsyncResult * result,GError ** error)353 g_unix_volume_mount_finish (GVolume        *volume,
354                             GAsyncResult  *result,
355                             GError       **error)
356 {
357   g_return_val_if_fail (g_task_is_valid (result, volume), FALSE);
358 
359   return g_task_propagate_boolean (G_TASK (result), error);
360 }
361 
362 static void
g_unix_volume_eject(GVolume * volume,GMountUnmountFlags flags,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)363 g_unix_volume_eject (GVolume             *volume,
364                      GMountUnmountFlags   flags,
365                      GCancellable        *cancellable,
366                      GAsyncReadyCallback  callback,
367                      gpointer             user_data)
368 {
369   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
370   const gchar *argv[] = { "eject", NULL, NULL };
371 
372   argv[1] = unix_volume->device_path;
373 
374   eject_mount_do (volume, cancellable, callback, user_data, argv, "[gio] eject volume");
375 }
376 
377 static gboolean
g_unix_volume_eject_finish(GVolume * volume,GAsyncResult * result,GError ** error)378 g_unix_volume_eject_finish (GVolume       *volume,
379                             GAsyncResult  *result,
380                             GError       **error)
381 {
382   g_return_val_if_fail (g_task_is_valid (result, volume), FALSE);
383 
384   return g_task_propagate_boolean (G_TASK (result), error);
385 }
386 
387 static gchar *
g_unix_volume_get_identifier(GVolume * volume,const gchar * kind)388 g_unix_volume_get_identifier (GVolume     *volume,
389                               const gchar *kind)
390 {
391   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
392 
393   if (unix_volume->identifier_type != NULL &&
394       strcmp (kind, unix_volume->identifier_type) == 0)
395     return g_strdup (unix_volume->identifier);
396 
397   return NULL;
398 }
399 
400 static gchar **
g_unix_volume_enumerate_identifiers(GVolume * volume)401 g_unix_volume_enumerate_identifiers (GVolume *volume)
402 {
403   GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
404   gchar **res;
405 
406   if (unix_volume->identifier_type)
407     {
408       res = g_new (gchar *, 2);
409       res[0] = g_strdup (unix_volume->identifier_type);
410       res[1] = NULL;
411     }
412   else
413     {
414       res = g_new (gchar *, 1);
415       res[0] = NULL;
416     }
417 
418   return res;
419 }
420 
421 static void
g_unix_volume_volume_iface_init(GVolumeIface * iface)422 g_unix_volume_volume_iface_init (GVolumeIface *iface)
423 {
424   iface->get_name = g_unix_volume_get_name;
425   iface->get_icon = g_unix_volume_get_icon;
426   iface->get_symbolic_icon = g_unix_volume_get_symbolic_icon;
427   iface->get_uuid = g_unix_volume_get_uuid;
428   iface->get_drive = g_unix_volume_get_drive;
429   iface->get_mount = g_unix_volume_get_mount;
430   iface->can_mount = g_unix_volume_can_mount;
431   iface->can_eject = g_unix_volume_can_eject;
432   iface->should_automount = g_unix_volume_should_automount;
433   iface->mount_fn = g_unix_volume_mount;
434   iface->mount_finish = g_unix_volume_mount_finish;
435   iface->eject = g_unix_volume_eject;
436   iface->eject_finish = g_unix_volume_eject_finish;
437   iface->get_identifier = g_unix_volume_get_identifier;
438   iface->enumerate_identifiers = g_unix_volume_enumerate_identifiers;
439 }
440