#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include "eeze_udev_private.h" #include "eeze_disk_private.h" #define EEZE_MOUNT_DEFAULT_OPTS "noexec,nosuid,utf8" EAPI int EEZE_EVENT_DISK_MOUNT = 0; EAPI int EEZE_EVENT_DISK_UNMOUNT = 0; EAPI int EEZE_EVENT_DISK_EJECT = 0; EAPI int EEZE_EVENT_DISK_ERROR = 0; static Ecore_Event_Handler *_mount_handler = NULL; Eina_List *eeze_events = NULL; /* * * PRIVATE * */ static void _eeze_disk_mount_error_free(void *data EINA_UNUSED, Eeze_Event_Disk_Error *de) { if (!de) return; eina_stringshare_del(de->message); free(de); } static void _eeze_disk_mount_error_handler(Eeze_Disk *disk, const char *error) { Eeze_Event_Disk_Error *de; ERR("%s", error); if (!(de = calloc(1, sizeof(Eeze_Event_Disk_Error)))) return; de->disk = disk; de->message = eina_stringshare_add(error); /* FIXME: placeholder since currently there are only mount-type errors */ ecore_event_add(EEZE_EVENT_DISK_ERROR, de, (Ecore_End_Cb)_eeze_disk_mount_error_free, NULL); } static Eina_Bool _eeze_disk_mount_result_handler(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Exe_Event_Del *ev) { Eeze_Disk *disk; Eina_List *l; Eeze_Event_Disk_Mount *e; if ((!ev) || (!ev->exe)) return ECORE_CALLBACK_RENEW; disk = ecore_exe_data_get(ev->exe); if ((!disk) || (!eeze_events) || (!(l = eina_list_data_find_list(eeze_events, disk)))) return ECORE_CALLBACK_RENEW; eeze_events = eina_list_remove_list(eeze_events, l); if (!disk->mounter) /* killed */ { disk->mount_status = EEZE_DISK_NULL; return ECORE_CALLBACK_RENEW; } if (disk->mount_status == EEZE_DISK_MOUNTING) { disk->mounter = NULL; if (!ev->exit_code) { disk->mounted = EINA_TRUE; e = malloc(sizeof(Eeze_Event_Disk_Mount)); EINA_SAFETY_ON_NULL_RETURN_VAL(e, ECORE_CALLBACK_RENEW); e->disk = disk; ecore_event_add(EEZE_EVENT_DISK_MOUNT, e, NULL, NULL); } else if (ev->exit_code & 2) _eeze_disk_mount_error_handler(disk, "system error (out of memory, cannot fork, no more loop devices)"); else if (ev->exit_code & 4) _eeze_disk_mount_error_handler(disk, "internal mount bug"); else if (ev->exit_code & 8) _eeze_disk_mount_error_handler(disk, "user interrupt"); else if (ev->exit_code & 16) _eeze_disk_mount_error_handler(disk, "problems writing or locking /etc/mtab"); else if (ev->exit_code & 32) _eeze_disk_mount_error_handler(disk, "mount failure"); else if (ev->exit_code & 64) _eeze_disk_mount_error_handler(disk, "some mount succeeded"); else _eeze_disk_mount_error_handler(disk, "incorrect invocation or permissions"); } else if (disk->mount_status == EEZE_DISK_UNMOUNTING) switch (ev->exit_code) { case 0: e = malloc(sizeof(Eeze_Event_Disk_Unmount)); EINA_SAFETY_ON_NULL_RETURN_VAL(e, ECORE_CALLBACK_RENEW); e->disk = disk; disk->mounter = NULL; disk->mounted = EINA_FALSE; ecore_event_add(EEZE_EVENT_DISK_UNMOUNT, e, NULL, NULL); break; default: if (disk->mount_fail_count++ < 3) { INF("Could not unmount disk, retrying"); disk->mounter = ecore_exe_run(eina_strbuf_string_get(disk->unmount_cmd), disk); eeze_events = eina_list_append(eeze_events, disk); } else { disk->mount_fail_count = 0; _eeze_disk_mount_error_handler(disk, "Maximimum number of mount-related failures reached"); } return ECORE_CALLBACK_RENEW; } else switch (ev->exit_code) { case 0: e = malloc(sizeof(Eeze_Event_Disk_Eject)); EINA_SAFETY_ON_NULL_RETURN_VAL(e, ECORE_CALLBACK_RENEW); e->disk = disk; disk->mounter = NULL; if (disk->mount_status & EEZE_DISK_UNMOUNTING) { disk->mount_status |= EEZE_DISK_UNMOUNTING; disk->mounted = EINA_FALSE; ecore_event_add(EEZE_EVENT_DISK_UNMOUNT, e, NULL, NULL); eeze_disk_eject(disk); } else ecore_event_add(EEZE_EVENT_DISK_EJECT, e, NULL, NULL); break; default: if (disk->mount_fail_count++ < 3) { INF("Could not eject disk, retrying"); if (disk->mount_status & EEZE_DISK_UNMOUNTING) disk->mounter = ecore_exe_run(eina_strbuf_string_get(disk->unmount_cmd), disk); else disk->mounter = ecore_exe_run(eina_strbuf_string_get(disk->eject_cmd), disk); eeze_events = eina_list_append(eeze_events, disk); } else { disk->mount_fail_count = 0; _eeze_disk_mount_error_handler(disk, "Maximimum number of mount-related failures reached"); } return ECORE_CALLBACK_RENEW; } return ECORE_CALLBACK_RENEW; } /* * * INVISIBLE * */ Eina_Bool eeze_mount_init(void) { EEZE_EVENT_DISK_MOUNT = ecore_event_type_new(); EEZE_EVENT_DISK_UNMOUNT = ecore_event_type_new(); EEZE_EVENT_DISK_EJECT = ecore_event_type_new(); EEZE_EVENT_DISK_ERROR = ecore_event_type_new(); _mount_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL, (Ecore_Event_Handler_Cb)_eeze_disk_mount_result_handler, NULL); return eeze_libmount_init(); } void eeze_mount_shutdown(void) { eeze_libmount_shutdown(); ecore_event_handler_del(_mount_handler); _mount_handler = NULL; } /* * * API * */ EAPI Eina_Bool eeze_disk_mounted_get(Eeze_Disk *disk) { EINA_SAFETY_ON_NULL_RETURN_VAL(disk, EINA_FALSE); return eeze_disk_libmount_mounted_get(disk); } EAPI Eina_Bool eeze_disk_mountopts_set(Eeze_Disk *disk, unsigned long opts) { EINA_SAFETY_ON_NULL_RETURN_VAL(disk, EINA_FALSE); if (opts != disk->mount_opts) disk->mount_cmd_changed = EINA_TRUE; disk->mount_opts = opts; if (opts & EEZE_DISK_MOUNTOPT_UID) disk->uid = getuid(); return EINA_TRUE; } EAPI unsigned long eeze_disk_mountopts_get(Eeze_Disk *disk) { EINA_SAFETY_ON_NULL_RETURN_VAL(disk, 0); return disk->mount_opts; } EAPI Eina_Bool eeze_disk_mount_wrapper_set(Eeze_Disk *disk, const char *wrapper) { EINA_SAFETY_ON_NULL_RETURN_VAL(disk, EINA_FALSE); if (wrapper) EINA_SAFETY_ON_TRUE_RETURN_VAL(!*wrapper, EINA_FALSE); else { eina_stringshare_del(disk->mount_wrapper); disk->mount_wrapper = NULL; return EINA_TRUE; } eina_stringshare_replace(&disk->mount_wrapper, wrapper); return EINA_TRUE; } EAPI const char * eeze_disk_mount_wrapper_get(Eeze_Disk *disk) { EINA_SAFETY_ON_NULL_RETURN_VAL(disk, NULL); return disk->mount_wrapper; } EAPI Eina_Bool eeze_disk_mount(Eeze_Disk *disk) { struct stat st; EINA_SAFETY_ON_NULL_RETURN_VAL(disk, EINA_FALSE); if ((!disk->mount_point) && eeze_disk_libmount_mounted_get(disk)) return EINA_FALSE; if (!disk->mount_cmd) disk->mount_cmd = eina_strbuf_new(); if (disk->mount_cmd_changed) { const char *dev, *str; eina_strbuf_string_free(disk->mount_cmd); dev = eeze_disk_uuid_get(disk); if (dev) str = "UUID="; else { dev = eeze_disk_devpath_get(disk); str = NULL; } if (!disk->mount_point) { const char *mp; /* here we attempt to guess the mount point using libmount */ mp = eeze_disk_libmount_mp_lookup_by_uuid(disk->cache.uuid); if (!mp) { const char *devpath; devpath = eeze_disk_devpath_get(disk); if (devpath) { mp = eeze_disk_libmount_mp_lookup_by_devpath(devpath); eina_stringshare_del(devpath); } } if (!eeze_disk_mount_point_set(disk, mp)) /* sometimes we fail */ return EINA_FALSE; } if ((!disk->mount_point) || (!disk->mount_point[0])) return EINA_FALSE; if (disk->mount_wrapper) eina_strbuf_append_printf(disk->mount_cmd, "%s ", disk->mount_wrapper); if (disk->mount_opts == EEZE_DISK_MOUNTOPT_DEFAULTS) eina_strbuf_append_printf(disk->mount_cmd, EEZE_MOUNT_BIN" -o "EEZE_MOUNT_DEFAULT_OPTS" %s%s %s", str ?: "", dev, disk->mount_point); else if (!disk->mount_opts) eina_strbuf_append_printf(disk->mount_cmd, EEZE_MOUNT_BIN" %s%s %s", str ?: "", dev, disk->mount_point); else { eina_strbuf_append(disk->mount_cmd, EEZE_MOUNT_BIN" -o "); /* trailing commas are okay */ if (disk->mount_opts & EEZE_DISK_MOUNTOPT_LOOP) eina_strbuf_append(disk->mount_cmd, "loop,"); if (disk->mount_opts & EEZE_DISK_MOUNTOPT_UTF8) { const char *fstype; eina_strbuf_append(disk->mount_cmd, "utf8,"); fstype = eeze_disk_fstype_get(disk); if (fstype && (!strcmp(fstype, "jfs"))) eina_strbuf_append(disk->mount_cmd, "iocharset=utf8,"); } if (disk->mount_opts & EEZE_DISK_MOUNTOPT_NOEXEC) eina_strbuf_append(disk->mount_cmd, "noexec,"); if (disk->mount_opts & EEZE_DISK_MOUNTOPT_NODEV) eina_strbuf_append(disk->mount_cmd, "nodev,"); if (disk->mount_opts & EEZE_DISK_MOUNTOPT_NOSUID) eina_strbuf_append(disk->mount_cmd, "nosuid,"); if (disk->mount_opts & EEZE_DISK_MOUNTOPT_REMOUNT) eina_strbuf_append(disk->mount_cmd, "remount,"); if (disk->mount_opts & EEZE_DISK_MOUNTOPT_UID) eina_strbuf_append_printf(disk->mount_cmd, "uid=%i,", (int)disk->uid); eina_strbuf_append_printf(disk->mount_cmd, " %s%s %s", str ?: "", dev, disk->mount_point); } disk->mount_cmd_changed = EINA_FALSE; } if (stat(disk->mount_point, &st)) { INF("Creating not-existing mount point directory '%s'", disk->mount_point); if (mkdir(disk->mount_point, S_IROTH | S_IWOTH | S_IXOTH)) ERR("Could not create directory: %s; hopefully this is handled by your mounter!", strerror(errno)); } else if (!S_ISDIR(st.st_mode)) { ERR("%s is not a directory!", disk->mount_point); return EINA_FALSE; } INF("Mounting: %s", eina_strbuf_string_get(disk->mount_cmd)); disk->mounter = ecore_exe_run(eina_strbuf_string_get(disk->mount_cmd), disk); if (!disk->mounter) return EINA_FALSE; eeze_events = eina_list_append(eeze_events, disk); disk->mount_status = EEZE_DISK_MOUNTING; return EINA_TRUE; } EAPI Eina_Bool eeze_disk_unmount(Eeze_Disk *disk) { EINA_SAFETY_ON_NULL_RETURN_VAL(disk, EINA_FALSE); if (!eeze_disk_libmount_mounted_get(disk)) return EINA_TRUE; if (!disk->unmount_cmd) disk->unmount_cmd = eina_strbuf_new(); if (disk->unmount_cmd_changed) { eina_strbuf_string_free(disk->unmount_cmd); if (disk->mount_wrapper) eina_strbuf_append_printf(disk->unmount_cmd, "%s ", disk->mount_wrapper); eina_strbuf_append_printf(disk->unmount_cmd, EEZE_UNMOUNT_BIN" %s", eeze_disk_devpath_get(disk)); disk->unmount_cmd_changed = EINA_FALSE; } INF("Unmounting: %s", eina_strbuf_string_get(disk->unmount_cmd)); disk->mounter = ecore_exe_run(eina_strbuf_string_get(disk->unmount_cmd), disk); if (!disk->mounter) return EINA_FALSE; eeze_events = eina_list_append(eeze_events, disk); disk->mount_status = EEZE_DISK_UNMOUNTING; return EINA_TRUE; } EAPI Eina_Bool eeze_disk_eject(Eeze_Disk *disk) { EINA_SAFETY_ON_NULL_RETURN_VAL(disk, EINA_FALSE); if (!disk->eject_cmd) { disk->eject_cmd = eina_strbuf_new(); if (disk->mount_wrapper) eina_strbuf_append_printf(disk->eject_cmd, "%s ", disk->mount_wrapper); eina_strbuf_append_printf(disk->eject_cmd, EEZE_EJECT_BIN" %s", eeze_disk_devpath_get(disk)); } INF("Ejecting: %s", eina_strbuf_string_get(disk->eject_cmd)); if (eeze_disk_libmount_mounted_get(disk)) { Eina_Bool ret; ret = eeze_disk_unmount(disk); if (ret) disk->mount_status |= EEZE_DISK_EJECTING; return ret; } disk->mounter = ecore_exe_run(eina_strbuf_string_get(disk->eject_cmd), disk); if (!disk->mounter) return EINA_FALSE; eeze_events = eina_list_append(eeze_events, disk); disk->mount_status = EEZE_DISK_EJECTING; return EINA_TRUE; } EAPI void eeze_disk_cancel(Eeze_Disk *disk) { EINA_SAFETY_ON_NULL_RETURN(disk); if ((!disk->mount_status) || (!disk->mounter)) return; disk->mount_status = EEZE_DISK_NULL; ecore_exe_kill(disk->mounter); disk->mounter = NULL; } EAPI const char * eeze_disk_mount_point_get(Eeze_Disk *disk) { const char *mp; EINA_SAFETY_ON_NULL_RETURN_VAL(disk, NULL); if (disk->mount_point) return disk->mount_point; mp = eeze_disk_libmount_mp_lookup_by_devpath(eeze_disk_devpath_get(disk)); if (mp) { disk->mount_point = eina_stringshare_add(mp); return disk->mount_point; } mp = eeze_disk_libmount_mp_lookup_by_uuid(eeze_disk_uuid_get(disk)); if (mp) { disk->mount_point = eina_stringshare_add(mp); return disk->mount_point; } mp = eeze_disk_libmount_mp_lookup_by_label(eeze_disk_label_get(disk)); if (mp) { disk->mount_point = eina_stringshare_add(mp); return disk->mount_point; } return NULL; } EAPI Eina_Bool eeze_disk_mount_point_set(Eeze_Disk *disk, const char *mount_point) { EINA_SAFETY_ON_NULL_RETURN_VAL(disk, EINA_FALSE); eina_stringshare_replace(&disk->mount_point, mount_point); disk->mount_cmd_changed = EINA_TRUE; disk->unmount_cmd_changed = EINA_TRUE; return EINA_TRUE; }