diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c index 27185c8fc..96733d9cc 100644 --- a/gio/gunixmounts.c +++ b/gio/gunixmounts.c @@ -148,7 +148,13 @@ G_DEFINE_BOXED_TYPE (GUnixMountPoint, g_unix_mount_point, g_unix_mount_point_copy, g_unix_mount_point_free) static GList *_g_get_unix_mounts (void); +static GUnixMountEntry **_g_unix_mounts_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_entries_out); static GList *_g_get_unix_mount_points (void); +static GUnixMountPoint **_g_unix_mount_points_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_points_out); static gboolean proc_mounts_watch_is_running (void); G_LOCK_DEFINE_STATIC (proc_mounts_source); @@ -206,6 +212,9 @@ static GSource *proc_mounts_watch_source = NULL; static struct libmnt_monitor *proc_mounts_monitor = NULL; #endif +static guint64 get_mounts_timestamp (void); +static guint64 get_mount_points_timestamp (void); + static gboolean is_in (const char *value, const char *set[]) { @@ -218,6 +227,45 @@ is_in (const char *value, const char *set[]) return FALSE; } +/* Marked as unused because these are only used on some platform variants, but + * working out the #if sequence for that would be too much for my little brain. */ +static GList *unix_mount_entry_array_free_to_list (GUnixMountEntry **entries, + size_t n_entries) G_GNUC_UNUSED; +static GList *unix_mount_point_array_free_to_list (GUnixMountPoint **points, + size_t n_points) G_GNUC_UNUSED; + +/* Helper to convert to a list for the old API. + * Steals ownership of the @entries array. */ +static GList * +unix_mount_entry_array_free_to_list (GUnixMountEntry **entries, + size_t n_entries) +{ + GList *l = NULL; + + for (size_t i = 0; i < n_entries; i++) + l = g_list_prepend (l, g_steal_pointer (&entries[i])); + + g_free (entries); + + return g_list_reverse (l); +} + +/* Helper to convert to a list for the old API. + * Steals ownership of the @entries array. */ +static GList * +unix_mount_point_array_free_to_list (GUnixMountPoint **points, + size_t n_points) +{ + GList *l = NULL; + + for (size_t i = 0; i < n_points; i++) + l = g_list_prepend (l, g_steal_pointer (&points[i])); + + g_free (points); + + return g_list_reverse (l); +} + /** * g_unix_is_mount_path_system_internal: * @mount_path: (type filename): a mount path, e.g. `/media/disk` or `/usr` @@ -501,17 +549,23 @@ create_unix_mount_point (const char *device_path, */ #define PROC_MOUNTINFO_PATH "/proc/self/mountinfo" -static GList * -_g_get_unix_mounts (void) +static GUnixMountEntry ** +_g_unix_mounts_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_entries_out) { struct libmnt_table *table = NULL; struct libmnt_iter* iter = NULL; struct libmnt_fs *fs = NULL; GUnixMountEntry *mount_entry = NULL; - GList *return_list = NULL; + GPtrArray *return_array = NULL; + if (time_read_out != NULL) + *time_read_out = get_mounts_timestamp (); + + return_array = g_ptr_array_new_null_terminated (0, (GDestroyNotify) g_unix_mount_free, TRUE); table = mnt_new_table (); - if (mnt_table_parse_mtab (table, NULL) < 0) + if (mnt_table_parse_mtab (table, table_path) < 0) goto out; iter = mnt_new_iter (MNT_ITER_FORWARD); @@ -541,14 +595,29 @@ _g_get_unix_mounts (void) mnt_fs_get_options (fs), is_read_only); - return_list = g_list_prepend (return_list, mount_entry); + g_ptr_array_add (return_array, g_steal_pointer (&mount_entry)); } mnt_free_iter (iter); out: mnt_free_table (table); - return g_list_reverse (return_list); + if (n_entries_out != NULL) + *n_entries_out = return_array->len; + + return (GUnixMountEntry **) g_ptr_array_free (g_steal_pointer (&return_array), FALSE); +} + +static GList * +_g_get_unix_mounts (void) +{ + GUnixMountEntry **entries = NULL; + size_t n_entries = 0; + + entries = _g_unix_mounts_get_from_file (NULL /* default libmount filename */, + NULL, &n_entries); + + return unix_mount_entry_array_free_to_list (g_steal_pointer (&entries), n_entries); } #else @@ -571,8 +640,10 @@ get_mtab_read_file (void) G_LOCK_DEFINE_STATIC(getmntent); #endif -static GList * -_g_get_unix_mounts (void) +static GUnixMountEntry ** +_g_unix_mounts_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_entries_out) { #ifdef HAVE_GETMNTENT_R struct mntent ent; @@ -580,19 +651,18 @@ _g_get_unix_mounts (void) #endif struct mntent *mntent; FILE *file; - const char *read_file; GUnixMountEntry *mount_entry; GHashTable *mounts_hash; - GList *return_list; - - read_file = get_mtab_read_file (); + GPtrArray *return_array = NULL; - file = setmntent (read_file, "re"); + if (time_read_out != NULL) + *time_read_out = get_mounts_timestamp (); + + file = setmntent (table_path, "re"); if (file == NULL) return NULL; - return_list = NULL; - + return_array = g_ptr_array_new_null_terminated (0, (GDestroyNotify) g_unix_mount_free, TRUE); mounts_hash = g_hash_table_new (g_str_hash, g_str_equal); #ifdef HAVE_GETMNTENT_R @@ -641,7 +711,7 @@ _g_get_unix_mounts (void) mount_entry->device_path, mount_entry->device_path); - return_list = g_list_prepend (return_list, mount_entry); + g_ptr_array_add (return_array, g_steal_pointer (&mount_entry)); } g_hash_table_destroy (mounts_hash); @@ -651,7 +721,21 @@ _g_get_unix_mounts (void) G_UNLOCK (getmntent); #endif - return g_list_reverse (return_list); + if (n_entries_out != NULL) + *n_entries_out = return_array->len; + + return (GUnixMountEntry **) g_ptr_array_free (g_steal_pointer (&return_array), FALSE); +} + +static GList * +_g_get_unix_mounts (void) +{ + GUnixMountEntry **entries = NULL; + size_t n_entries = 0; + + entries = _g_unix_mounts_get_from_file (get_mtab_read_file (), NULL, &n_entries); + + return unix_mount_entry_array_free_to_list (g_steal_pointer (&entries), n_entries); } #endif /* HAVE_LIBMOUNT */ @@ -718,23 +802,26 @@ get_mtab_monitor_file (void) return get_mtab_read_file (); } -static GList * -_g_get_unix_mounts (void) +static GUnixMountEntry ** +_g_unix_mounts_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_entries_out) { struct mnttab mntent; FILE *file; const char *read_file; GUnixMountEntry *mount_entry; - GList *return_list; - - read_file = get_mtab_read_file (); - + GPtrArray *return_array = NULL; + + if (time_read_out != NULL) + *time_read_out = get_mounts_timestamp (); + file = setmntent (read_file, "re"); if (file == NULL) return NULL; - - return_list = NULL; - + + return_array = g_ptr_array_new_null_terminated (0, (GDestroyNotify) g_unix_mount_free, TRUE); + G_LOCK (getmntent); while (! getmntent (file, &mntent)) { @@ -752,14 +839,28 @@ _g_get_unix_mounts (void) mntent.mnt_opts, is_read_only); - return_list = g_list_prepend (return_list, mount_entry); + g_ptr_array_add (return_array, g_steal_pointer (&mount_entry)); } endmntent (file); G_UNLOCK (getmntent); - - return g_list_reverse (return_list); + + if (n_entries_out != NULL) + *n_entries_out = return_array->len; + + return (GUnixMountEntry **) g_ptr_array_free (g_steal_pointer (&return_array), FALSE); +} + +static GList * +_g_get_unix_mounts (void) +{ + GUnixMountEntry **entries = NULL; + size_t n_entries = 0; + + entries = _g_unix_mounts_get_from_file (get_mtab_read_file (), NULL, &n_entries); + + return unix_mount_entry_array_free_to_list (g_steal_pointer (&entries), n_entries); } /* mntctl.h (AIX) {{{2 */ @@ -832,6 +933,20 @@ _g_get_unix_mounts (void) return g_list_reverse (return_list); } +static GUnixMountEntry ** +_g_unix_mounts_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_entries_out) +{ + /* Not supported on mntctl() systems. */ + if (time_read_out != NULL) + *time_read_out = 0; + if (n_entries_out != NULL) + *n_entries_out = 0; + + return NULL; +} + /* sys/mount.h {{{2 */ #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H) @@ -905,6 +1020,20 @@ _g_get_unix_mounts (void) return g_list_reverse (return_list); } +static GUnixMountEntry ** +_g_unix_mounts_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_entries_out) +{ + /* Not supported on getvfsstat()/getfsstat() systems. */ + if (time_read_out != NULL) + *time_read_out = 0; + if (n_entries_out != NULL) + *n_entries_out = 0; + + return NULL; +} + /* Interix {{{2 */ #elif defined(__INTERIX) @@ -962,6 +1091,20 @@ _g_get_unix_mounts (void) return return_list; } +static GUnixMountEntry ** +_g_unix_mounts_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_entries_out) +{ + /* Not supported on Interix systems. */ + if (time_read_out != NULL) + *time_read_out = 0; + if (n_entries_out != NULL) + *n_entries_out = 0; + + return NULL; +} + /* QNX {{{2 */ #elif defined (HAVE_QNX) @@ -972,6 +1115,20 @@ get_mtab_monitor_file (void) return NULL; } +static GUnixMountEntry ** +_g_unix_mounts_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_entries_out) +{ + /* Not implemented, as per _g_get_unix_mounts() below */ + if (time_read_out != NULL) + *time_read_out = 0; + if (n_entries_out != NULL) + *n_entries_out = 0; + + return NULL; +} + static GList * _g_get_unix_mounts (void) { @@ -1015,17 +1172,23 @@ get_fstab_file (void) #ifdef HAVE_LIBMOUNT -static GList * -_g_get_unix_mount_points (void) +static GUnixMountPoint ** +_g_unix_mount_points_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_points_out) { struct libmnt_table *table = NULL; struct libmnt_iter* iter = NULL; struct libmnt_fs *fs = NULL; GUnixMountPoint *mount_point = NULL; - GList *return_list = NULL; + GPtrArray *return_array = NULL; + if (time_read_out != NULL) + *time_read_out = get_mount_points_timestamp (); + + return_array = g_ptr_array_new_null_terminated (0, (GDestroyNotify) g_unix_mount_point_free, TRUE); table = mnt_new_table (); - if (mnt_table_parse_fstab (table, NULL) < 0) + if (mnt_table_parse_fstab (table, table_path) < 0) goto out; iter = mnt_new_iter (MNT_ITER_FORWARD); @@ -1089,20 +1252,37 @@ _g_get_unix_mount_points (void) if (mount_options) g_free (mount_options); - return_list = g_list_prepend (return_list, mount_point); + g_ptr_array_add (return_array, g_steal_pointer (&mount_point)); } mnt_free_iter (iter); out: mnt_free_table (table); - return g_list_reverse (return_list); + if (n_points_out != NULL) + *n_points_out = return_array->len; + + return (GUnixMountPoint **) g_ptr_array_free (g_steal_pointer (&return_array), FALSE); +} + +static GList * +_g_get_unix_mount_points (void) +{ + GUnixMountPoint **points = NULL; + size_t n_points = 0; + + points = _g_unix_mount_points_get_from_file (NULL /* default libmount filename */, + NULL, &n_points); + + return unix_mount_point_array_free_to_list (g_steal_pointer (&points), n_points); } #else -static GList * -_g_get_unix_mount_points (void) +static GUnixMountPoint ** +_g_unix_mount_points_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_points_out) { #ifdef HAVE_GETMNTENT_R struct mntent ent; @@ -1112,16 +1292,21 @@ _g_get_unix_mount_points (void) FILE *file; char *read_file; GUnixMountPoint *mount_point; - GList *return_list; - - read_file = get_fstab_file (); - - file = setmntent (read_file, "re"); - if (file == NULL) - return NULL; + GPtrArray *return_array = NULL; + + if (time_read_out != NULL) + *time_read_out = get_mount_points_timestamp (); + + file = setmntent (table_path, "re"); + if (file == NULL) + { + if (n_points_out != NULL) + *n_points_out = 0; + return NULL; + } + + return_array = g_ptr_array_new_null_terminated (0, (GDestroyNotify) g_unix_mount_point_free, TRUE); - return_list = NULL; - #ifdef HAVE_GETMNTENT_R while ((mntent = getmntent_r (file, &ent, buf, sizeof (buf))) != NULL) #else @@ -1177,7 +1362,7 @@ _g_get_unix_mount_points (void) is_user_mountable, is_loopback); - return_list = g_list_prepend (return_list, mount_point); + g_ptr_array_add (return_array, g_steal_pointer (&mount_point)); } endmntent (file); @@ -1185,8 +1370,23 @@ _g_get_unix_mount_points (void) #ifndef HAVE_GETMNTENT_R G_UNLOCK (getmntent); #endif - - return g_list_reverse (return_list); + + if (n_points_out != NULL) + *n_points_out = return_array->len; + + return (GUnixMountPoint **) g_ptr_array_free (g_steal_pointer (&return_array), FALSE); +} + +static GList * +_g_get_unix_mount_points (void) +{ + GUnixMountPoint **points = NULL; + size_t n_points = 0; + + points = _g_unix_mount_points_get_from_file (get_fstab_file (), + NULL, &n_points); + + return unix_mount_point_array_free_to_list (g_steal_pointer (&points), n_points); } #endif /* HAVE_LIBMOUNT */ @@ -1194,22 +1394,29 @@ _g_get_unix_mount_points (void) /* mnttab.h {{{2 */ #elif defined (HAVE_SYS_MNTTAB_H) -static GList * -_g_get_unix_mount_points (void) +static GUnixMountPoint ** +_g_unix_mount_points_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_points_out) { struct mnttab mntent; FILE *file; char *read_file; GUnixMountPoint *mount_point; - GList *return_list; - - read_file = get_fstab_file (); - - file = setmntent (read_file, "re"); - if (file == NULL) - return NULL; + GPtrArray *return_array = NULL; - return_list = NULL; + if (time_read_out != NULL) + *time_read_out = get_mount_points_timestamp (); + + file = setmntent (table_path, "re"); + if (file == NULL) + { + if (n_points_out != NULL) + *n_points_out = 0; + return NULL; + } + + return_array = g_ptr_array_new_null_terminated (0, (GDestroyNotify) g_unix_mount_point_free, TRUE); G_LOCK (getmntent); while (! getmntent (file, &mntent)) @@ -1249,13 +1456,28 @@ _g_get_unix_mount_points (void) is_user_mountable, is_loopback); - return_list = g_list_prepend (return_list, mount_point); + g_ptr_array_add (return_array, g_steal_pointer (&mount_point)); } endmntent (file); G_UNLOCK (getmntent); - - return g_list_reverse (return_list); + + if (n_points_out != NULL) + *n_points_out = return_array->len; + + return (GUnixMountPoint **) g_ptr_array_free (g_steal_pointer (&return_array), FALSE); +} + +static GList * +_g_get_unix_mount_points (void) +{ + GUnixMountPoint **points = NULL; + size_t n_points = 0; + + points = _g_unix_mount_points_get_from_file (get_fstab_file (), + NULL, &n_points); + + return unix_mount_point_array_free_to_list (g_steal_pointer (&points), n_points); } /* mntctl.h (AIX) {{{2 */ @@ -1368,24 +1590,31 @@ aix_fs_get (FILE *fd, return 0; } -static GList * -_g_get_unix_mount_points (void) +static GUnixMountPoint ** +_g_unix_mount_points_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_points_out) { struct mntent *mntent; FILE *file; char *read_file; GUnixMountPoint *mount_point; AixMountTableEntry mntent; - GList *return_list; - - read_file = get_fstab_file (); - - file = setmntent (read_file, "re"); + GPtrArray *return_array = NULL; + + if (time_read_out != NULL) + *time_read_out = get_mount_points_timestamp (); + + file = setmntent (table_path, "re"); if (file == NULL) - return NULL; - - return_list = NULL; - + { + if (n_points_out != NULL) + *n_points_out = 0; + return NULL; + } + + return_array = g_ptr_array_new_null_terminated (0, (GDestroyNotify) g_unix_mount_point_free, TRUE); + while (!aix_fs_get (file, &mntent)) { if (strcmp ("cdrfs", mntent.mnt_fstype) == 0) @@ -1398,13 +1627,28 @@ _g_get_unix_mount_points (void) TRUE, FALSE); - return_list = g_list_prepend (return_list, mount_point); + g_ptr_array_add (return_array, g_steal_pointer (&mount_point)); } } endmntent (file); - - return g_list_reverse (return_list); + + if (n_points_out != NULL) + *n_points_out = return_array->len; + + return (GUnixMountPoint **) g_ptr_array_free (g_steal_pointer (&return_array), FALSE); +} + +static GList * +_g_get_unix_mount_points (void) +{ + GUnixMountPoint **points = NULL; + size_t n_points = 0; + + points = _g_unix_mount_points_get_from_file (get_fstab_file (), + NULL, &n_points); + + return unix_mount_point_array_free_to_list (g_steal_pointer (&points), n_points); } #elif (defined(HAVE_GETVFSSTAT) || defined(HAVE_GETFSSTAT)) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H) @@ -1495,20 +1739,18 @@ _g_get_unix_mount_points (void) return g_list_reverse (return_list); } -/* Interix {{{2 */ -#elif defined(__INTERIX) -static GList * -_g_get_unix_mount_points (void) -{ - return _g_get_unix_mounts (); -} -/* QNX {{{2 */ -#elif defined (HAVE_QNX) -static GList * -_g_get_unix_mount_points (void) +static GUnixMountPoint ** +_g_unix_mount_points_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_points_out) { - return _g_get_unix_mounts (); + /* Not supported on getfsent() systems. */ + if (time_read_out != NULL) + *time_read_out = 0; + if (n_points_out != NULL) + *n_points_out = 0; + return NULL; } /* Common code {{{2 */ @@ -1588,6 +1830,37 @@ g_unix_mounts_get (guint64 *time_read) return _g_get_unix_mounts (); } +/** + * g_unix_mounts_get_from_file: + * @table_path: path to the mounts table file (for example `/proc/self/mountinfo`) + * @time_read_out: (optional) (out caller-allocates): return location for the + * modification time of @table_path + * @n_entries_out: (optional) (out caller-allocates): return location for the + * number of mount entries returned + * + * Gets an array of [struct@Gio.UnixMountEntry]s containing the Unix mounts + * listed in @table_path. + * + * This is a generalized version of g_unix_mounts_get(), mainly intended for + * internal testing use. Note that g_unix_mounts_get() may parse multiple + * hierarchical table files, so this function is not a direct superset of its + * functionality. + * + * If there is an error reading or parsing the file, `NULL` will be returned + * and both out parameters will be set to `0`. + * + * Returns: (transfer full) (array length=n_entries_out) (nullable): mount + * entries, or `NULL` if there was an error loading them + * Since: 2.82 + */ +GUnixMountEntry ** +g_unix_mounts_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_entries_out) +{ + return _g_unix_mounts_get_from_file (table_path, time_read_out, n_entries_out); +} + /** * g_unix_mount_at: * @mount_path: (type filename): path for a possible unix mount. @@ -1724,6 +1997,37 @@ g_unix_mount_points_get (guint64 *time_read) return mnt_pts; } +/** + * g_unix_mount_points_get_from_file: + * @table_path: path to the mount points table file (for example `/etc/fstab`) + * @time_read_out: (optional) (out caller-allocates): return location for the + * modification time of @table_path + * @n_points_out: (optional) (out caller-allocates): return location for the + * number of mount points returned + * + * Gets an array of [struct@Gio.UnixMountPoint]s containing the Unix mount + * points listed in @table_path. + * + * This is a generalized version of g_unix_mount_points_get(), mainly intended + * for internal testing use. Note that g_unix_mount_points_get() may parse + * multiple hierarchical table files, so this function is not a direct superset + * of its functionality. + * + * If there is an error reading or parsing the file, `NULL` will be returned + * and both out parameters will be set to `0`. + * + * Returns: (transfer full) (array length=n_points_out) (nullable): mount + * points, or `NULL` if there was an error loading them + * Since: 2.82 + */ +GUnixMountPoint ** +g_unix_mount_points_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_points_out) +{ + return _g_unix_mount_points_get_from_file (table_path, time_read_out, n_points_out); +} + /** * g_unix_mount_point_at: * @mount_path: (type filename): path for a possible unix mount point. diff --git a/gio/gunixmounts.h b/gio/gunixmounts.h index b3595b55a..32bf16db5 100644 --- a/gio/gunixmounts.h +++ b/gio/gunixmounts.h @@ -24,6 +24,7 @@ #define __G_UNIX_MOUNTS_H__ #include +#include G_BEGIN_DECLS @@ -134,11 +135,19 @@ GIcon * g_unix_mount_point_guess_symbolic_icon (GUnixMountPoint *mount GIO_AVAILABLE_IN_ALL GList * g_unix_mount_points_get (guint64 *time_read); +GIO_AVAILABLE_IN_2_82 +GUnixMountPoint **g_unix_mount_points_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_points_out); GIO_AVAILABLE_IN_2_66 GUnixMountPoint *g_unix_mount_point_at (const char *mount_path, guint64 *time_read); GIO_AVAILABLE_IN_ALL GList * g_unix_mounts_get (guint64 *time_read); +GIO_AVAILABLE_IN_2_82 +GUnixMountEntry **g_unix_mounts_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_entries_out); GIO_AVAILABLE_IN_ALL GUnixMountEntry *g_unix_mount_at (const char *mount_path, guint64 *time_read); diff --git a/gio/tests/unix-mounts.c b/gio/tests/unix-mounts.c index e3cc6c18c..ea0df8b22 100644 --- a/gio/tests/unix-mounts.c +++ b/gio/tests/unix-mounts.c @@ -18,6 +18,8 @@ * Public License along with this library; if not, see . */ +#include "config.h" + #include #ifndef G_OS_UNIX @@ -26,6 +28,12 @@ #include #include + +#ifdef HAVE_XLOCALE_H +/* Needed on macOS and FreeBSD for uselocale() */ +#include +#endif + #include #include #include @@ -50,6 +58,279 @@ test_is_system_device_path (void) g_assert_false (g_unix_is_system_device_path ("/")); } +static void +assert_cmp_icon (GIcon *icon, + gboolean expected_icon) +{ + if (expected_icon) + { + char *icon_str = NULL; + + /* While it would be nice to compare the icon value, that would make these + * tests depend on the icon themes installed. So just compare nullness. */ + g_assert_nonnull (icon); + icon_str = g_icon_to_string (icon); + g_test_message ("Icon: %s", icon_str); + g_free (icon_str); + } + else + { + g_assert_null (icon); + } +} + +static void +test_get_mount_points (void) +{ + GUnixMountPoint **points = NULL; + uint64_t time_read = 0; + size_t n_points = 0; + int fd = -1; + char *tmp_file = NULL; + gboolean res; +#ifdef HAVE_USELOCALE + locale_t original_locale; + locale_t new_locale; + locale_t result; +#endif + const char *fake_fstab = "# Some comment\n" + "/dev/mapper/fedora-root / ext4 defaults,x-systemd.device-timeout=0 1 1\n" + "UUID=1234-ABCD /boot ext4 defaults 1 2\n" + "UUID=ABCD-1234 /boot/efi vfat umask=0077,shortname=winnt,ro 0 2\n" + "/dev/mapper/fedora-home /home ext4 defaults,x-systemd.device-timeout=0 1 2\n" + "/dev/mapper/fedora-swap none swap defaults,x-systemd.device-timeout=0 0 0\n" + "/dev/mapper/unused none ext4 defaults\n"; + const struct + { + const char *device_path; + const char *fs_type; + const char *options; + gboolean is_readonly; + gboolean is_user_mountable; + gboolean is_loopback; + gboolean guessed_icon; + gboolean guessed_symbolic_icon; + const char *guessed_name; + gboolean guessed_can_eject; + } + expected_points[] = + { + { + .device_path = "/dev/mapper/fedora-root", + .fs_type = "ext4", + .options = "defaults,x-systemd.device-timeout=0", + .is_readonly = FALSE, + .is_user_mountable = FALSE, + .is_loopback = FALSE, + .guessed_icon = TRUE, + .guessed_symbolic_icon = TRUE, + .guessed_name = "Filesystem root", + .guessed_can_eject = FALSE, + }, + { + .device_path = "UUID=1234-ABCD", + .fs_type = "ext4", + .options = "defaults", + .is_readonly = FALSE, + .is_user_mountable = FALSE, + .is_loopback = FALSE, + .guessed_icon = TRUE, + .guessed_symbolic_icon = TRUE, + .guessed_name = "boot", + .guessed_can_eject = FALSE, + }, + { + .device_path = "UUID=ABCD-1234", + .fs_type = "vfat", + .options = "umask=0077,shortname=winnt,ro", + .is_readonly = TRUE, + .is_user_mountable = FALSE, + .is_loopback = FALSE, + .guessed_icon = TRUE, + .guessed_symbolic_icon = TRUE, + .guessed_name = "efi", + .guessed_can_eject = FALSE, + }, + { + .device_path = "/dev/mapper/fedora-home", + .fs_type = "ext4", + .options = "defaults,x-systemd.device-timeout=0", + .is_readonly = FALSE, + .is_user_mountable = FALSE, + .is_loopback = FALSE, + .guessed_icon = TRUE, + .guessed_symbolic_icon = TRUE, + .guessed_name = "home", + .guessed_can_eject = FALSE, + }, + /* the swap partition is ignored */ + /* as is the ignored unused partition */ + }; + + g_test_summary ("Basic test of g_unix_mount_points_get_from_file()"); + + fd = g_file_open_tmp ("unix-mounts-XXXXXX", &tmp_file, NULL); + g_assert (fd != -1); + close (fd); + + res = g_file_set_contents (tmp_file, fake_fstab, -1, NULL); + g_assert (res); + + points = g_unix_mount_points_get_from_file (tmp_file, &time_read, &n_points); + + if (points == NULL) + { + /* Some platforms may not support parsing a specific mount point file */ + g_assert_cmpuint (time_read, ==, 0); + g_assert_cmpuint (n_points, ==, 0); + g_test_skip ("Parsing mount points from a file not supported on this platform"); + return; + } + + g_assert_nonnull (points); + + /* Check the properties of the mount points. This needs to be done in a known + * locale, because the guessed mount point name is translatable. */ + g_assert_cmpuint (n_points, ==, G_N_ELEMENTS (expected_points)); + +#ifdef HAVE_USELOCALE + original_locale = uselocale ((locale_t) 0); + g_assert_true (original_locale != (locale_t) 0); + new_locale = newlocale (LC_ALL_MASK, "C", (locale_t) 0); + g_assert_true (new_locale != (locale_t) 0); + result = uselocale (new_locale); + g_assert_true (result == original_locale); +#endif /* HAVE_USELOCALE */ + + for (size_t i = 0; i < n_points; i++) + { + GIcon *icon = NULL; + char *name = NULL; + + g_assert_cmpstr (g_unix_mount_point_get_device_path (points[i]), ==, expected_points[i].device_path); + g_assert_cmpstr (g_unix_mount_point_get_fs_type (points[i]), ==, expected_points[i].fs_type); + g_assert_cmpstr (g_unix_mount_point_get_options (points[i]), ==, expected_points[i].options); + g_assert_true (g_unix_mount_point_is_readonly (points[i]) == expected_points[i].is_readonly); + g_assert_true (g_unix_mount_point_is_user_mountable (points[i]) == expected_points[i].is_user_mountable); + g_assert_true (g_unix_mount_point_is_loopback (points[i]) == expected_points[i].is_loopback); + + icon = g_unix_mount_point_guess_icon (points[i]); + assert_cmp_icon (icon, expected_points[i].guessed_icon); + g_clear_object (&icon); + + icon = g_unix_mount_point_guess_symbolic_icon (points[i]); + assert_cmp_icon (icon, expected_points[i].guessed_symbolic_icon); + g_clear_object (&icon); + + name = g_unix_mount_point_guess_name (points[i]); +#ifdef HAVE_USELOCALE + g_assert_cmpstr (name, ==, expected_points[i].guessed_name); +#else + g_assert_nonnull (name); +#endif + g_free (name); + + g_assert_true (g_unix_mount_point_guess_can_eject (points[i]) == expected_points[i].guessed_can_eject); + } + + for (size_t i = 0; i < n_points; i++) + g_unix_mount_point_free (points[i]); + g_free (points); + g_free (tmp_file); + +#ifdef HAVE_USELOCALE + result = uselocale (original_locale); + g_assert_true (result == new_locale); + freelocale (new_locale); +#endif +} + +static void +test_get_mount_entries (void) +{ + GUnixMountEntry **entries = NULL; + uint64_t time_read = 0; + size_t n_entries = 0; + int fd = -1; + char *tmp_file = NULL; + gboolean res; + const char *fake_mtab = "# Some comment\n" + "67 1 253:1 / / rw,relatime shared:1 - ext4 /dev/mapper/fedora-root rw,seclabel\n" + "35 67 0:6 / /dev rw,nosuid shared:2 - devtmpfs devtmpfs rw,seclabel,size=4096k,nr_inodes=1995515,mode=755,inode64\n" + "1537 1080 253:1 /usr/share/fonts /run/host/fonts ro,nosuid,nodev,relatime master:1 - ext4 /dev/mapper/fedora-root rw,seclabel\n"; + const struct + { + const char *device_path; + const char *fs_type; + const char *mount_path; + const char *options; + const char *root_path; + } + expected_entries[] = + { + { + .device_path = "/dev/mapper/fedora-root", + .fs_type = "ext4", + .mount_path = "/", + .options = "rw,relatime,seclabel", + .root_path = "/", + }, + { + .device_path = "devtmpfs", + .fs_type = "devtmpfs", + .mount_path = "/dev", + .options = "rw,nosuid,seclabel,size=4096k,nr_inodes=1995515,mode=755,inode64", + .root_path = "/", + }, + { + .device_path = "/dev/mapper/fedora-root", + .fs_type = "ext4", + .mount_path = "/run/host/fonts", + .options = "ro,nosuid,nodev,relatime,seclabel", + .root_path = "/usr/share/fonts", + }, + }; + + g_test_summary ("Basic test of g_unix_mounts_get_from_file()"); + + fd = g_file_open_tmp ("unix-mounts-XXXXXX", &tmp_file, NULL); + g_assert (fd != -1); + close (fd); + + res = g_file_set_contents (tmp_file, fake_mtab, -1, NULL); + g_assert (res); + + entries = g_unix_mounts_get_from_file (tmp_file, &time_read, &n_entries); + + if (entries == NULL) + { + /* Some platforms may not support parsing a specific mount entry file */ + g_assert_cmpuint (time_read, ==, 0); + g_assert_cmpuint (n_entries, ==, 0); + g_test_skip ("Parsing mount entries from a file not supported on this platform"); + return; + } + + g_assert_nonnull (entries); + + /* Check the properties of the mount entries. */ + g_assert_cmpuint (n_entries, ==, G_N_ELEMENTS (expected_entries)); + + for (size_t i = 0; i < n_entries; i++) + { + g_assert_cmpstr (g_unix_mount_get_device_path (entries[i]), ==, expected_entries[i].device_path); + g_assert_cmpstr (g_unix_mount_get_fs_type (entries[i]), ==, expected_entries[i].fs_type); + g_assert_cmpstr (g_unix_mount_get_mount_path (entries[i]), ==, expected_entries[i].mount_path); + g_assert_cmpstr (g_unix_mount_get_options (entries[i]), ==, expected_entries[i].options); + g_assert_cmpstr (g_unix_mount_get_root_path (entries[i]), ==, expected_entries[i].root_path); + } + + for (size_t i = 0; i < n_entries; i++) + g_unix_mount_free (entries[i]); + g_free (entries); + g_free (tmp_file); +} + int main (int argc, char *argv[]) @@ -60,6 +341,8 @@ main (int argc, g_test_add_func ("/unix-mounts/is-system-fs-type", test_is_system_fs_type); g_test_add_func ("/unix-mounts/is-system-device-path", test_is_system_device_path); + g_test_add_func ("/unix-mounts/get-mount-points", test_get_mount_points); + g_test_add_func ("/unix-mounts/get-mount-entries", test_get_mount_entries); return g_test_run (); }