1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 
5 #include <unistd.h>
6 #include <sys/stat.h>
7 #include <sys/types.h>
8 #include <errno.h>
9 
10 #include <Ecore.h>
11 #include <Eeze.h>
12 #include <Eeze_Disk.h>
13 
14 #include "eeze_udev_private.h"
15 #include "eeze_disk_private.h"
16 
17 #define EEZE_MOUNT_DEFAULT_OPTS "noexec,nosuid,utf8"
18 
19 EAPI int EEZE_EVENT_DISK_MOUNT = 0;
20 EAPI int EEZE_EVENT_DISK_UNMOUNT = 0;
21 EAPI int EEZE_EVENT_DISK_EJECT = 0;
22 EAPI int EEZE_EVENT_DISK_ERROR = 0;
23 static Ecore_Event_Handler *_mount_handler = NULL;
24 Eina_List *eeze_events = NULL;
25 
26 /*
27  *
28  * PRIVATE
29  *
30  */
31 
32 static void
_eeze_disk_mount_error_free(void * data EINA_UNUSED,Eeze_Event_Disk_Error * de)33 _eeze_disk_mount_error_free(void *data EINA_UNUSED, Eeze_Event_Disk_Error *de)
34 {
35    if (!de)
36      return;
37 
38    eina_stringshare_del(de->message);
39    free(de);
40 }
41 
42 static void
_eeze_disk_mount_error_handler(Eeze_Disk * disk,const char * error)43 _eeze_disk_mount_error_handler(Eeze_Disk *disk, const char *error)
44 {
45    Eeze_Event_Disk_Error *de;
46 
47    ERR("%s", error);
48    if (!(de = calloc(1, sizeof(Eeze_Event_Disk_Error))))
49      return;
50 
51    de->disk = disk;
52    de->message = eina_stringshare_add(error);
53    /* FIXME: placeholder since currently there are only mount-type errors */
54    ecore_event_add(EEZE_EVENT_DISK_ERROR, de, (Ecore_End_Cb)_eeze_disk_mount_error_free, NULL);
55 }
56 
57 static Eina_Bool
_eeze_disk_mount_result_handler(void * data EINA_UNUSED,int type EINA_UNUSED,Ecore_Exe_Event_Del * ev)58 _eeze_disk_mount_result_handler(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Exe_Event_Del *ev)
59 {
60    Eeze_Disk *disk;
61    Eina_List *l;
62    Eeze_Event_Disk_Mount *e;
63 
64    if ((!ev) || (!ev->exe))
65      return ECORE_CALLBACK_RENEW;
66    disk = ecore_exe_data_get(ev->exe);
67 
68    if ((!disk) || (!eeze_events) || (!(l = eina_list_data_find_list(eeze_events, disk))))
69      return ECORE_CALLBACK_RENEW;
70 
71    eeze_events = eina_list_remove_list(eeze_events, l);
72    if (!disk->mounter) /* killed */
73      {
74         disk->mount_status = EEZE_DISK_NULL;
75         return ECORE_CALLBACK_RENEW;
76      }
77    if (disk->mount_status == EEZE_DISK_MOUNTING)
78      {
79         disk->mounter = NULL;
80         if (!ev->exit_code)
81           {
82               disk->mounted = EINA_TRUE;
83               e = malloc(sizeof(Eeze_Event_Disk_Mount));
84               EINA_SAFETY_ON_NULL_RETURN_VAL(e, ECORE_CALLBACK_RENEW);
85               e->disk = disk;
86               ecore_event_add(EEZE_EVENT_DISK_MOUNT, e, NULL, NULL);
87           }
88         else if (ev->exit_code & 2)
89            _eeze_disk_mount_error_handler(disk, "system error (out of memory, cannot fork, no more loop devices)");
90         else if (ev->exit_code & 4)
91            _eeze_disk_mount_error_handler(disk, "internal mount bug");
92         else if (ev->exit_code & 8)
93            _eeze_disk_mount_error_handler(disk, "user interrupt");
94         else if (ev->exit_code & 16)
95            _eeze_disk_mount_error_handler(disk, "problems writing or locking /etc/mtab");
96         else if (ev->exit_code & 32)
97            _eeze_disk_mount_error_handler(disk, "mount failure");
98         else if (ev->exit_code & 64)
99            _eeze_disk_mount_error_handler(disk, "some mount succeeded");
100         else
101            _eeze_disk_mount_error_handler(disk, "incorrect invocation or permissions");
102      }
103    else if (disk->mount_status == EEZE_DISK_UNMOUNTING)
104      switch (ev->exit_code)
105        {
106         case 0:
107           e = malloc(sizeof(Eeze_Event_Disk_Unmount));
108           EINA_SAFETY_ON_NULL_RETURN_VAL(e, ECORE_CALLBACK_RENEW);
109           e->disk = disk;
110           disk->mounter = NULL;
111           disk->mounted = EINA_FALSE;
112           ecore_event_add(EEZE_EVENT_DISK_UNMOUNT, e, NULL, NULL);
113           break;
114 
115         default:
116           if (disk->mount_fail_count++ < 3)
117             {
118                INF("Could not unmount disk, retrying");
119                disk->mounter = ecore_exe_run(eina_strbuf_string_get(disk->unmount_cmd), disk);
120                eeze_events = eina_list_append(eeze_events, disk);
121             }
122           else
123             {
124                disk->mount_fail_count = 0;
125                _eeze_disk_mount_error_handler(disk, "Maximum number of mount-related failures reached");
126             }
127           return ECORE_CALLBACK_RENEW;
128        }
129      else
130        switch (ev->exit_code)
131          {
132           case 0:
133             e = malloc(sizeof(Eeze_Event_Disk_Eject));
134             EINA_SAFETY_ON_NULL_RETURN_VAL(e, ECORE_CALLBACK_RENEW);
135             e->disk = disk;
136             disk->mounter = NULL;
137             if (disk->mount_status & EEZE_DISK_UNMOUNTING)
138               {
139                  disk->mount_status |= EEZE_DISK_UNMOUNTING;
140                  disk->mounted = EINA_FALSE;
141                  ecore_event_add(EEZE_EVENT_DISK_UNMOUNT, e, NULL, NULL);
142                  eeze_disk_eject(disk);
143               }
144             else
145               ecore_event_add(EEZE_EVENT_DISK_EJECT, e, NULL, NULL);
146             break;
147 
148           default:
149             if (disk->mount_fail_count++ < 3)
150               {
151                  INF("Could not eject disk, retrying");
152                  if (disk->mount_status & EEZE_DISK_UNMOUNTING)
153                    disk->mounter = ecore_exe_run(eina_strbuf_string_get(disk->unmount_cmd), disk);
154                  else
155                    disk->mounter = ecore_exe_run(eina_strbuf_string_get(disk->eject_cmd), disk);
156                  eeze_events = eina_list_append(eeze_events, disk);
157               }
158            else
159              {
160                 disk->mount_fail_count = 0;
161                 _eeze_disk_mount_error_handler(disk, "Maximum number of mount-related failures reached");
162              }
163             return ECORE_CALLBACK_RENEW;
164          }
165    return ECORE_CALLBACK_RENEW;
166 }
167 
168 /*
169  *
170  * INVISIBLE
171  *
172  */
173 
174 Eina_Bool
eeze_mount_init(void)175 eeze_mount_init(void)
176 {
177    EEZE_EVENT_DISK_MOUNT = ecore_event_type_new();
178    EEZE_EVENT_DISK_UNMOUNT = ecore_event_type_new();
179    EEZE_EVENT_DISK_EJECT = ecore_event_type_new();
180    EEZE_EVENT_DISK_ERROR = ecore_event_type_new();
181    _mount_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL,
182                                            (Ecore_Event_Handler_Cb)_eeze_disk_mount_result_handler, NULL);
183    return eeze_libmount_init();
184 }
185 
186 void
eeze_mount_shutdown(void)187 eeze_mount_shutdown(void)
188 {
189    ecore_event_type_flush(EEZE_EVENT_DISK_MOUNT,
190                           EEZE_EVENT_DISK_UNMOUNT,
191                           EEZE_EVENT_DISK_EJECT,
192                           EEZE_EVENT_DISK_ERROR);
193    eeze_libmount_shutdown();
194    ecore_event_handler_del(_mount_handler);
195    _mount_handler = NULL;
196 }
197 
198 /*
199  *
200  * API
201  *
202  */
203 
204 EAPI Eina_Bool
eeze_disk_mounted_get(Eeze_Disk * disk)205 eeze_disk_mounted_get(Eeze_Disk *disk)
206 {
207    EINA_SAFETY_ON_NULL_RETURN_VAL(disk, EINA_FALSE);
208 
209    return eeze_disk_libmount_mounted_get(disk);
210 }
211 
212 EAPI Eina_Bool
eeze_disk_mountopts_set(Eeze_Disk * disk,unsigned long opts)213 eeze_disk_mountopts_set(Eeze_Disk *disk, unsigned long opts)
214 {
215    EINA_SAFETY_ON_NULL_RETURN_VAL(disk, EINA_FALSE);
216    if (opts != disk->mount_opts)
217      disk->mount_cmd_changed = EINA_TRUE;
218    disk->mount_opts = opts;
219    if (opts & EEZE_DISK_MOUNTOPT_UID)
220      disk->uid = getuid();
221    return EINA_TRUE;
222 }
223 
224 EAPI unsigned long
eeze_disk_mountopts_get(Eeze_Disk * disk)225 eeze_disk_mountopts_get(Eeze_Disk *disk)
226 {
227    EINA_SAFETY_ON_NULL_RETURN_VAL(disk, 0);
228    return disk->mount_opts;
229 }
230 
231 EAPI Eina_Bool
eeze_disk_mount_wrapper_set(Eeze_Disk * disk,const char * wrapper)232 eeze_disk_mount_wrapper_set(Eeze_Disk *disk, const char *wrapper)
233 {
234    EINA_SAFETY_ON_NULL_RETURN_VAL(disk, EINA_FALSE);
235    if (wrapper) EINA_SAFETY_ON_TRUE_RETURN_VAL(!*wrapper, EINA_FALSE);
236    else
237      {
238         eina_stringshare_del(disk->mount_wrapper);
239         disk->mount_wrapper = NULL;
240         return EINA_TRUE;
241      }
242    eina_stringshare_replace(&disk->mount_wrapper, wrapper);
243    return EINA_TRUE;
244 }
245 
246 EAPI const char *
eeze_disk_mount_wrapper_get(Eeze_Disk * disk)247 eeze_disk_mount_wrapper_get(Eeze_Disk *disk)
248 {
249    EINA_SAFETY_ON_NULL_RETURN_VAL(disk, NULL);
250    return disk->mount_wrapper;
251 }
252 
253 EAPI Eina_Bool
eeze_disk_mount(Eeze_Disk * disk)254 eeze_disk_mount(Eeze_Disk *disk)
255 {
256    struct stat st;
257    EINA_SAFETY_ON_NULL_RETURN_VAL(disk, EINA_FALSE);
258 
259    if (!disk->mount_point)
260      return EINA_FALSE;
261    if (eeze_disk_libmount_mounted_get(disk))
262      return EINA_FALSE;
263 
264    if (!disk->mount_cmd)
265      disk->mount_cmd = eina_strbuf_new();
266 
267    if (disk->mount_cmd_changed)
268      {
269         const char *dev, *str;
270         eina_strbuf_string_free(disk->mount_cmd);
271         dev = eeze_disk_uuid_get(disk);
272         if (dev) str = "UUID=";
273         else
274           {
275              dev = eeze_disk_devpath_get(disk);
276              str = NULL;
277           }
278 
279         if (!disk->mount_point)
280           {
281              const char *mp;
282              /* here we attempt to guess the mount point using libmount */
283              mp = eeze_disk_libmount_mp_lookup_by_uuid(disk->cache.uuid);
284              if (!mp)
285                {
286                   const char *devpath;
287 
288                   devpath = eeze_disk_devpath_get(disk);
289                   if (devpath)
290                     {
291                        mp = eeze_disk_libmount_mp_lookup_by_devpath(devpath);
292                        eina_stringshare_del(devpath);
293                     }
294                }
295              if (!eeze_disk_mount_point_set(disk, mp))
296                /* sometimes we fail */
297                return EINA_FALSE;
298           }
299 
300         if ((!disk->mount_point) || (!disk->mount_point[0])) return EINA_FALSE;
301         if (disk->mount_wrapper)
302           eina_strbuf_append_printf(disk->mount_cmd, "%s ", disk->mount_wrapper);
303         if (disk->mount_opts == EEZE_DISK_MOUNTOPT_DEFAULTS)
304           eina_strbuf_append_printf(disk->mount_cmd, EEZE_MOUNT_BIN" -o "EEZE_MOUNT_DEFAULT_OPTS" %s%s %s", str ? str : "", dev, disk->mount_point);
305         else if (!disk->mount_opts)
306           eina_strbuf_append_printf(disk->mount_cmd, EEZE_MOUNT_BIN" %s%s %s", str ? str : "", dev, disk->mount_point);
307         else
308           {
309              eina_strbuf_append(disk->mount_cmd, EEZE_MOUNT_BIN" -o ");
310              /* trailing commas are okay */
311              if (disk->mount_opts & EEZE_DISK_MOUNTOPT_LOOP)
312                eina_strbuf_append(disk->mount_cmd, "loop,");
313              if (disk->mount_opts & EEZE_DISK_MOUNTOPT_UTF8)
314                {
315                   const char *fstype;
316                   eina_strbuf_append(disk->mount_cmd, "utf8,");
317                   fstype = eeze_disk_fstype_get(disk);
318                   if (fstype && (!strcmp(fstype, "jfs")))
319                     eina_strbuf_append(disk->mount_cmd, "iocharset=utf8,");
320                }
321              if (disk->mount_opts & EEZE_DISK_MOUNTOPT_NOEXEC)
322                eina_strbuf_append(disk->mount_cmd, "noexec,");
323              if (disk->mount_opts & EEZE_DISK_MOUNTOPT_NODEV)
324                eina_strbuf_append(disk->mount_cmd, "nodev,");
325              if (disk->mount_opts & EEZE_DISK_MOUNTOPT_NOSUID)
326                eina_strbuf_append(disk->mount_cmd, "nosuid,");
327              if (disk->mount_opts & EEZE_DISK_MOUNTOPT_REMOUNT)
328                eina_strbuf_append(disk->mount_cmd, "remount,");
329              if (disk->mount_opts & EEZE_DISK_MOUNTOPT_UID)
330                eina_strbuf_append_printf(disk->mount_cmd, "uid=%i,", (int)disk->uid);
331              eina_strbuf_append_printf(disk->mount_cmd, " %s%s %s", str ? str : "", dev, disk->mount_point);
332           }
333         disk->mount_cmd_changed = EINA_FALSE;
334      }
335 
336    if (stat(disk->mount_point, &st))
337      {
338         INF("Creating not-existing mount point directory '%s'", disk->mount_point);
339         if (mkdir(disk->mount_point, S_IROTH | S_IWOTH | S_IXOTH))
340           ERR("Could not create directory: %s; hopefully this is handled by your mounter!", strerror(errno));
341      }
342    else if (!S_ISDIR(st.st_mode))
343      {
344         ERR("%s is not a directory!", disk->mount_point);
345         return EINA_FALSE;
346      }
347    INF("Mounting: %s", eina_strbuf_string_get(disk->mount_cmd));
348    disk->mounter = ecore_exe_run(eina_strbuf_string_get(disk->mount_cmd), disk);
349    if (!disk->mounter)
350      return EINA_FALSE;
351    eeze_events = eina_list_append(eeze_events, disk);
352    disk->mount_status = EEZE_DISK_MOUNTING;
353 
354    return EINA_TRUE;
355 }
356 
357 EAPI Eina_Bool
eeze_disk_unmount(Eeze_Disk * disk)358 eeze_disk_unmount(Eeze_Disk *disk)
359 {
360    EINA_SAFETY_ON_NULL_RETURN_VAL(disk, EINA_FALSE);
361 
362    if (!eeze_disk_libmount_mounted_get(disk))
363      return EINA_TRUE;
364 
365    if (!disk->unmount_cmd)
366      disk->unmount_cmd = eina_strbuf_new();
367 
368    if (disk->unmount_cmd_changed)
369      {
370         eina_strbuf_string_free(disk->unmount_cmd);
371         if (disk->mount_wrapper)
372           eina_strbuf_append_printf(disk->unmount_cmd, "%s ", disk->mount_wrapper);
373         eina_strbuf_append_printf(disk->unmount_cmd, EEZE_UNMOUNT_BIN" %s", eeze_disk_devpath_get(disk));
374         disk->unmount_cmd_changed = EINA_FALSE;
375      }
376 
377    INF("Unmounting: %s", eina_strbuf_string_get(disk->unmount_cmd));
378    disk->mounter = ecore_exe_run(eina_strbuf_string_get(disk->unmount_cmd), disk);
379    if (!disk->mounter)
380      return EINA_FALSE;
381 
382    eeze_events = eina_list_append(eeze_events, disk);
383    disk->mount_status = EEZE_DISK_UNMOUNTING;
384    return EINA_TRUE;
385 }
386 
387 EAPI Eina_Bool
eeze_disk_eject(Eeze_Disk * disk)388 eeze_disk_eject(Eeze_Disk *disk)
389 {
390    EINA_SAFETY_ON_NULL_RETURN_VAL(disk, EINA_FALSE);
391 
392    if (!disk->eject_cmd)
393      {
394         disk->eject_cmd = eina_strbuf_new();
395         if (disk->mount_wrapper)
396           eina_strbuf_append_printf(disk->eject_cmd, "%s ", disk->mount_wrapper);
397         eina_strbuf_append_printf(disk->eject_cmd, EEZE_EJECT_BIN" %s", eeze_disk_devpath_get(disk));
398      }
399 
400    INF("Ejecting: %s", eina_strbuf_string_get(disk->eject_cmd));
401    if (eeze_disk_libmount_mounted_get(disk))
402      {
403         Eina_Bool ret;
404 
405         ret = eeze_disk_unmount(disk);
406         if (ret) disk->mount_status |= EEZE_DISK_EJECTING;
407         return ret;
408      }
409    disk->mounter = ecore_exe_run(eina_strbuf_string_get(disk->eject_cmd), disk);
410    if (!disk->mounter)
411      return EINA_FALSE;
412 
413    eeze_events = eina_list_append(eeze_events, disk);
414    disk->mount_status = EEZE_DISK_EJECTING;
415    return EINA_TRUE;
416 }
417 
418 EAPI void
eeze_disk_cancel(Eeze_Disk * disk)419 eeze_disk_cancel(Eeze_Disk *disk)
420 {
421    EINA_SAFETY_ON_NULL_RETURN(disk);
422    if ((!disk->mount_status) || (!disk->mounter)) return;
423    disk->mount_status = EEZE_DISK_NULL;
424    ecore_exe_kill(disk->mounter);
425    disk->mounter = NULL;
426 }
427 
428 EAPI const char *
eeze_disk_mount_point_get(Eeze_Disk * disk)429 eeze_disk_mount_point_get(Eeze_Disk *disk)
430 {
431    const char *mp;
432    EINA_SAFETY_ON_NULL_RETURN_VAL(disk, NULL);
433 
434    if (disk->mount_point)
435      return disk->mount_point;
436 
437    mp = eeze_disk_libmount_mp_lookup_by_devpath(eeze_disk_devpath_get(disk));
438    if (mp)
439      {
440         disk->mount_point = eina_stringshare_add(mp);
441         return disk->mount_point;
442      }
443    mp = eeze_disk_libmount_mp_lookup_by_uuid(eeze_disk_uuid_get(disk));
444    if (mp)
445      {
446         disk->mount_point = eina_stringshare_add(mp);
447         return disk->mount_point;
448      }
449    mp = eeze_disk_libmount_mp_lookup_by_label(eeze_disk_label_get(disk));
450    if (mp)
451      {
452         disk->mount_point = eina_stringshare_add(mp);
453         return disk->mount_point;
454      }
455    return NULL;
456 }
457 
458 EAPI Eina_Bool
eeze_disk_mount_point_set(Eeze_Disk * disk,const char * mount_point)459 eeze_disk_mount_point_set(Eeze_Disk *disk, const char *mount_point)
460 {
461    EINA_SAFETY_ON_NULL_RETURN_VAL(disk, EINA_FALSE);
462 
463    eina_stringshare_replace(&disk->mount_point, mount_point);
464    disk->mount_cmd_changed = EINA_TRUE;
465    disk->unmount_cmd_changed = EINA_TRUE;
466    return EINA_TRUE;
467 }
468