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