1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4
5 #ifndef USE_UNSTABLE_LIBMOUNT_API
6 # define USE_UNSTABLE_LIBMOUNT_API 1
7 #endif
8
9 #include <Ecore.h>
10 #include <Eeze.h>
11 #include <Eeze_Disk.h>
12 #include <libmount.h>
13 #include <unistd.h>
14
15 #include "eeze_udev_private.h"
16 #include "eeze_disk_private.h"
17
18 /*
19 *
20 * PRIVATE
21 *
22 */
23
24 static struct libmnt_optmap eeze_optmap[] =
25 {
26 { "loop[=]", EEZE_DISK_MOUNTOPT_LOOP, 0 },
27 { "utf8", EEZE_DISK_MOUNTOPT_UTF8, 0 },
28 { "noexec", EEZE_DISK_MOUNTOPT_NOEXEC, 0 },
29 { "nosuid", EEZE_DISK_MOUNTOPT_NOSUID, 0 },
30 { "remount", EEZE_DISK_MOUNTOPT_REMOUNT, 0 },
31 { "uid[=]", EEZE_DISK_MOUNTOPT_UID, 0 },
32 { "nodev", EEZE_DISK_MOUNTOPT_NODEV, 0 },
33 { NULL, 0, 0 }
34 };
35 typedef struct libmnt_table libmnt_table;
36 typedef struct libmnt_lock libmnt_lock;
37 typedef struct libmnt_fs libmnt_fs;
38 typedef struct libmnt_cache libmnt_cache;
39 static Ecore_File_Monitor *_mtab_mon = NULL;
40 static Ecore_File_Monitor *_fstab_mon = NULL;
41 static Eina_Bool _watching = EINA_FALSE;
42 static Eina_Bool _mtab_scan_active = EINA_FALSE;
43 static Eina_Bool _mtab_locked = EINA_FALSE;
44 static Eina_Bool _fstab_scan_active = EINA_FALSE;
45 static libmnt_cache *_eeze_mount_mtab_cache = NULL;
46 static libmnt_cache *_eeze_mount_fstab_cache = NULL;
47 static libmnt_table *_eeze_mount_mtab = NULL;
48 static libmnt_table *_eeze_mount_fstab = NULL;
49 static libmnt_lock *_eeze_mtab_lock = NULL;
50 extern Eina_List *_eeze_disks;
51
52 static libmnt_table *_eeze_mount_tab_parse(const char *filename);
53 static void _eeze_mount_tab_watcher(void *data, Ecore_File_Monitor *mon EINA_UNUSED, Ecore_File_Event event EINA_UNUSED, const char *path);
54
55 static Eina_Bool
_eeze_mount_lock_mtab(void)56 _eeze_mount_lock_mtab(void)
57 {
58 // DBG("Locking mlock: %s", mnt_lock_get_linkfile(_eeze_mtab_lock));
59 if (EINA_LIKELY(access("/etc/mtab", W_OK)))
60 {
61 INF("Insufficient privs for mtab lock, continuing without lock");
62 return EINA_TRUE;
63 }
64 if (mnt_lock_file(_eeze_mtab_lock))
65 {
66 ERR("Couldn't lock mtab!");
67 return EINA_FALSE;
68 }
69 _mtab_locked = EINA_TRUE;
70 return EINA_TRUE;
71 }
72
73 static void
_eeze_mount_unlock_mtab(void)74 _eeze_mount_unlock_mtab(void)
75 {
76 // DBG("Unlocking mlock: %s", mnt_lock_get_linkfile(_eeze_mtab_lock));
77 if (_mtab_locked) mnt_unlock_file(_eeze_mtab_lock);
78 _mtab_locked = EINA_FALSE;
79 }
80
81
82 static int
_eeze_mount_tab_parse_errcb(libmnt_table * tab EINA_UNUSED,const char * filename,int line)83 _eeze_mount_tab_parse_errcb(libmnt_table *tab EINA_UNUSED, const char *filename, int line)
84 {
85 ERR("%s:%d: could not parse line!", filename, line); /* most worthless error reporting ever. */
86 return -1;
87 }
88
89 /*
90 * I could use mnt_new_table_from_file() but this way gives much more detailed output
91 * on failure so why not
92 */
93 static libmnt_table *
_eeze_mount_tab_parse(const char * filename)94 _eeze_mount_tab_parse(const char *filename)
95 {
96 libmnt_table *tab;
97
98 if (!(tab = mnt_new_table())) return NULL;
99 if (mnt_table_set_parser_errcb(tab, _eeze_mount_tab_parse_errcb))
100 {
101 ERR("Alloc!");
102 mnt_free_table(tab);
103 return NULL;
104 }
105
106 if (!mnt_table_parse_file(tab, filename))
107 return tab;
108
109 mnt_free_table(tab);
110 return NULL;
111 }
112
113 static void
_eeze_mount_tab_watcher(void * data,Ecore_File_Monitor * mon EINA_UNUSED,Ecore_File_Event event EINA_UNUSED,const char * path)114 _eeze_mount_tab_watcher(void *data, Ecore_File_Monitor *mon EINA_UNUSED, Ecore_File_Event event EINA_UNUSED, const char *path)
115 {
116 libmnt_table *bak;
117
118 if (
119 ((_mtab_scan_active) && (data)) || /* mtab has non-null data to avoid needing strcmp */
120 ((_fstab_scan_active) && (!data))
121 )
122 /* prevent scans from triggering a scan */
123 return;
124
125 bak = _eeze_mount_mtab;
126 if (data)
127 if (!_eeze_mount_lock_mtab())
128 { /* FIXME: maybe queue job here? */
129 ERR("Losing events...");
130 return;
131 }
132 _eeze_mount_mtab = _eeze_mount_tab_parse(path);
133 if (data)
134 _eeze_mount_unlock_mtab();
135 if (!_eeze_mount_mtab)
136 {
137 ERR("Could not parse %s! keeping old tab...", path);
138 goto error;
139 }
140 if (data)
141 {
142 Eina_List *l;
143 Eeze_Disk *disk;
144
145 /* catch externally initiated mounts on existing disks by comparing known mount state to current state */
146 EINA_LIST_FOREACH(_eeze_disks, l, disk)
147 {
148 Eina_Bool mounted;
149
150 mounted = disk->mounted;
151
152 if ((eeze_disk_libmount_mounted_get(disk) != mounted) && (!disk->mount_status))
153 {
154 if (!mounted)
155 {
156 Eeze_Event_Disk_Mount *e;
157 e = malloc(sizeof(Eeze_Event_Disk_Mount));
158 if (e)
159 {
160 e->disk = disk;
161 ecore_event_add(EEZE_EVENT_DISK_MOUNT, e, NULL, NULL);
162 }
163 }
164 else
165 {
166 Eeze_Event_Disk_Unmount *e;
167 e = malloc(sizeof(Eeze_Event_Disk_Unmount));
168 if (e)
169 {
170 e->disk = disk;
171 ecore_event_add(EEZE_EVENT_DISK_UNMOUNT, e, NULL, NULL);
172 }
173 }
174 }
175 }
176 }
177
178 mnt_free_table(bak);
179 if (data)
180 {
181 mnt_free_cache(_eeze_mount_mtab_cache);
182 _eeze_mount_mtab_cache = mnt_new_cache();
183 mnt_table_set_cache(_eeze_mount_mtab, _eeze_mount_mtab_cache);
184 }
185 else
186 {
187 mnt_free_cache(_eeze_mount_fstab_cache);
188 _eeze_mount_fstab_cache = mnt_new_cache();
189 mnt_table_set_cache(_eeze_mount_fstab, _eeze_mount_fstab_cache);
190 }
191 return;
192
193 error:
194 mnt_free_table(_eeze_mount_mtab);
195 _eeze_mount_mtab = bak;
196 }
197
198 /*
199 *
200 * INVISIBLE
201 *
202 */
203
204 Eina_Bool
eeze_libmount_init(void)205 eeze_libmount_init(void)
206 {
207 if (_eeze_mtab_lock)
208 return EINA_TRUE;
209 if (!(_eeze_mtab_lock = mnt_new_lock("/etc/mtab", 0)))
210 return EINA_FALSE;
211 return EINA_TRUE;
212 }
213
214 void
eeze_libmount_shutdown(void)215 eeze_libmount_shutdown(void)
216 {
217 if (_eeze_mount_fstab)
218 {
219 mnt_free_table(_eeze_mount_fstab);
220 mnt_free_cache(_eeze_mount_fstab_cache);
221 }
222 if (_eeze_mount_mtab)
223 {
224 mnt_free_table(_eeze_mount_mtab);
225 mnt_free_cache(_eeze_mount_mtab_cache);
226 }
227 eeze_mount_tabs_unwatch();
228 if (!_eeze_mtab_lock)
229 return;
230
231 mnt_unlock_file(_eeze_mtab_lock);
232 mnt_free_lock(_eeze_mtab_lock);
233 _eeze_mtab_lock = NULL;
234 }
235
236 unsigned long
eeze_disk_libmount_opts_get(Eeze_Disk * disk)237 eeze_disk_libmount_opts_get(Eeze_Disk *disk)
238 {
239 libmnt_fs *mnt;
240 const char *opts;
241 unsigned long f = 0;
242
243 if (!eeze_mount_mtab_scan() || !eeze_mount_fstab_scan())
244 return 0;
245
246 mnt = mnt_table_find_tag(_eeze_mount_mtab, "UUID", eeze_disk_uuid_get(disk), MNT_ITER_BACKWARD);
247 if (!mnt)
248 mnt = mnt_table_find_tag(_eeze_mount_fstab, "UUID", eeze_disk_uuid_get(disk), MNT_ITER_BACKWARD);
249
250 if (!mnt) return 0;
251
252 opts = mnt_fs_get_fs_options(mnt);
253 if (!opts) return 0;
254 if (!mnt_optstr_get_flags(opts, &f, eeze_optmap)) return 0;
255 return f;
256 }
257
258 /*
259 * helper function to return whether a disk is mounted
260 */
261 Eina_Bool
eeze_disk_libmount_mounted_get(Eeze_Disk * disk)262 eeze_disk_libmount_mounted_get(Eeze_Disk *disk)
263 {
264 libmnt_fs *mnt;
265
266 if (!disk)
267 return EINA_FALSE;
268
269 if (!eeze_mount_mtab_scan() || !eeze_mount_fstab_scan())
270 return EINA_FALSE;
271
272 mnt = mnt_table_find_srcpath(_eeze_mount_mtab, eeze_disk_devpath_get(disk), MNT_ITER_BACKWARD);
273 if (!mnt)
274 {
275 disk->mounted = EINA_FALSE;
276 return EINA_FALSE;
277 }
278
279 eina_stringshare_replace(&disk->mount_point, mnt_fs_get_target(mnt));
280 disk->mounted = EINA_TRUE;
281 return EINA_TRUE;
282 }
283
284
285 /*
286 * helper function to return the device that is mounted at a mount point
287 */
288 const char *
eeze_disk_libmount_mp_find_source(const char * mount_point)289 eeze_disk_libmount_mp_find_source(const char *mount_point)
290 {
291 libmnt_fs *mnt;
292
293 if (!mount_point)
294 return NULL;
295
296 if (!eeze_mount_mtab_scan() || !eeze_mount_fstab_scan())
297 return NULL;
298
299 mnt = mnt_table_find_target(_eeze_mount_mtab, mount_point, MNT_ITER_BACKWARD);
300 if (!mnt)
301 mnt = mnt_table_find_target(_eeze_mount_fstab, mount_point, MNT_ITER_BACKWARD);
302
303 if (!mnt)
304 return NULL;
305
306 return mnt_fs_get_source(mnt);
307 }
308
309 /*
310 * helper function to return a mount point from a uuid
311 */
312 const char *
eeze_disk_libmount_mp_lookup_by_uuid(const char * uuid)313 eeze_disk_libmount_mp_lookup_by_uuid(const char *uuid)
314 {
315 libmnt_fs *mnt;
316
317 if (!uuid)
318 return NULL;
319
320 if (!eeze_mount_mtab_scan() || !eeze_mount_fstab_scan())
321 return NULL;
322
323 mnt = mnt_table_find_tag(_eeze_mount_fstab, "UUID", uuid, MNT_ITER_BACKWARD);
324
325 if (!mnt)
326 return NULL;
327
328 return mnt_fs_get_target(mnt);
329 }
330
331 /*
332 * helper function to return a mount point from a label
333 */
334 const char *
eeze_disk_libmount_mp_lookup_by_label(const char * label)335 eeze_disk_libmount_mp_lookup_by_label(const char *label)
336 {
337 libmnt_fs *mnt;
338
339 if (!label)
340 return NULL;
341
342 if (!eeze_mount_mtab_scan() || !eeze_mount_fstab_scan())
343 return NULL;
344
345 mnt = mnt_table_find_tag(_eeze_mount_fstab, "LABEL", label, MNT_ITER_BACKWARD);
346
347 if (!mnt)
348 return NULL;
349
350 return mnt_fs_get_target(mnt);
351 }
352
353 /*
354 * helper function to return a mount point from a /dev/ path
355 */
356 const char *
eeze_disk_libmount_mp_lookup_by_devpath(const char * devpath)357 eeze_disk_libmount_mp_lookup_by_devpath(const char *devpath)
358 {
359 libmnt_fs *mnt;
360
361 if (!devpath)
362 return NULL;
363
364 if (!eeze_mount_mtab_scan() || !eeze_mount_fstab_scan())
365 return NULL;
366
367 mnt = mnt_table_find_srcpath(_eeze_mount_mtab, devpath, MNT_ITER_BACKWARD);
368 if (!mnt)
369 mnt = mnt_table_find_srcpath(_eeze_mount_fstab, devpath, MNT_ITER_BACKWARD);
370
371 if (!mnt)
372 return NULL;
373
374 return mnt_fs_get_target(mnt);
375 }
376
377 /*
378 *
379 * API
380 *
381 */
382
383 EAPI Eina_Bool
eeze_mount_tabs_watch(void)384 eeze_mount_tabs_watch(void)
385 {
386 libmnt_table *bak;
387
388 if (_watching)
389 return EINA_TRUE;
390
391 if (!_eeze_mount_lock_mtab())
392 return EINA_FALSE;
393
394 bak = _eeze_mount_tab_parse("/etc/mtab");
395 _eeze_mount_unlock_mtab();
396 if (!bak)
397 goto error;
398
399 mnt_free_table(_eeze_mount_mtab);
400 _eeze_mount_mtab = bak;
401 if (!(bak = _eeze_mount_tab_parse("/etc/fstab")))
402 goto error;
403
404 mnt_free_table(_eeze_mount_fstab);
405 _eeze_mount_fstab = bak;
406
407 _eeze_mount_mtab_cache = mnt_new_cache();
408 mnt_table_set_cache(_eeze_mount_mtab, _eeze_mount_mtab_cache);
409
410 _eeze_mount_fstab_cache = mnt_new_cache();
411 mnt_table_set_cache(_eeze_mount_fstab, _eeze_mount_fstab_cache);
412
413 _mtab_mon = ecore_file_monitor_add("/etc/mtab", _eeze_mount_tab_watcher, (void*)1);
414 _fstab_mon = ecore_file_monitor_add("/etc/fstab", _eeze_mount_tab_watcher, NULL);
415 _watching = EINA_TRUE;
416
417 return EINA_TRUE;
418
419 error:
420 if (!_eeze_mount_mtab)
421 ERR("Could not parse /etc/mtab!");
422 else
423 {
424 ERR("Could not parse /etc/fstab!");
425 mnt_free_table(_eeze_mount_mtab);
426 }
427 return EINA_FALSE;
428 }
429
430 EAPI void
eeze_mount_tabs_unwatch(void)431 eeze_mount_tabs_unwatch(void)
432 {
433 if (!_watching)
434 return;
435
436 ecore_file_monitor_del(_mtab_mon);
437 _mtab_mon = NULL;
438 ecore_file_monitor_del(_fstab_mon);
439 _fstab_mon = NULL;
440 _watching = EINA_FALSE;
441 }
442
443 EAPI Eina_Bool
eeze_mount_mtab_scan(void)444 eeze_mount_mtab_scan(void)
445 {
446 libmnt_table *bak;
447
448 if (_watching)
449 return EINA_TRUE;
450
451 if (!_eeze_mount_lock_mtab())
452 return EINA_FALSE;
453 bak = _eeze_mount_tab_parse("/etc/mtab");
454 _eeze_mount_unlock_mtab();
455 if (!bak)
456 goto error;
457 if (_eeze_mount_mtab)
458 {
459 mnt_free_table(_eeze_mount_mtab);
460 mnt_free_cache(_eeze_mount_mtab_cache);
461 }
462 _eeze_mount_mtab = bak;
463 _eeze_mount_mtab_cache = mnt_new_cache();
464 mnt_table_set_cache(_eeze_mount_mtab, _eeze_mount_mtab_cache);
465
466 return EINA_TRUE;
467
468 error:
469 return EINA_FALSE;
470 }
471
472 EAPI Eina_Bool
eeze_mount_fstab_scan(void)473 eeze_mount_fstab_scan(void)
474 {
475 libmnt_table *bak;
476 if (_watching)
477 return EINA_TRUE;
478
479 bak = _eeze_mount_tab_parse("/etc/fstab");
480 if (!bak)
481 goto error;
482 if (_eeze_mount_fstab)
483 {
484 mnt_free_table(_eeze_mount_fstab);
485 mnt_free_cache(_eeze_mount_fstab_cache);
486 }
487 _eeze_mount_fstab = bak;
488 _eeze_mount_fstab_cache = mnt_new_cache();
489 mnt_table_set_cache(_eeze_mount_fstab, _eeze_mount_fstab_cache);
490
491 return EINA_TRUE;
492
493 error:
494 return EINA_FALSE;
495 }
496