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