From 5040cf19436851a7ad8f7231705d44a831cb3836 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Sat, 20 Jul 2024 14:53:57 +0200 Subject: [PATCH 1/3] gunixmounts: Add mount point/entry getters from files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don’t expect users to start querying the fstab or mtab by explicitly loading data from those file paths. These functions are mainly intended to prove a controllable entry point into the `gunixmounts.c` code for unit testing. It means we can provide a file with controllable contents in order to test the mount entry/point code on. See: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4155 Signed-off-by: Philip Withnall --- gio/gunixmounts.c | 497 +++++++++++++++++++++++++++++++++++++++------- gio/gunixmounts.h | 9 + 2 files changed, 430 insertions(+), 76 deletions(-) diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c index 27185c8fc..8e410c90c 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,6 +1739,19 @@ _g_get_unix_mount_points (void) return g_list_reverse (return_list); } + +static GUnixMountPoint ** +_g_unix_mount_points_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_points_out) +{ + /* 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; +} /* Interix {{{2 */ #elif defined(__INTERIX) static GList * @@ -1503,6 +1760,19 @@ _g_get_unix_mount_points (void) return _g_get_unix_mounts (); } +static GUnixMountPoint ** +_g_unix_mount_points_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_points_out) +{ + /* Not supported on Interix systems. */ + if (time_read_out != NULL) + *time_read_out = 0; + if (n_points_out != NULL) + *n_points_out = 0; + return NULL; +} + /* QNX {{{2 */ #elif defined (HAVE_QNX) static GList * @@ -1511,6 +1781,19 @@ _g_get_unix_mount_points (void) return _g_get_unix_mounts (); } +static GUnixMountPoint ** +_g_unix_mount_points_get_from_file (const char *table_path, + uint64_t *time_read_out, + size_t *n_points_out) +{ + /* Not supported on QNX systems. */ + if (time_read_out != NULL) + *time_read_out = 0; + if (n_points_out != NULL) + *n_points_out = 0; + return NULL; +} + /* Common code {{{2 */ #else #error No g_get_mount_table() implementation for system @@ -1588,6 +1871,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 +2038,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 11fc5f6d9..d35655f76 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); From 04ee0111716b9047d50c4c4aa4e791dca40128da Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Sat, 20 Jul 2024 16:59:19 +0200 Subject: [PATCH 2/3] tests: Add tests for handling of fstab and mtab files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While GLib doesn’t parse these files, it does provide API to access the fields from them, and does implement some logic based on options fields in them. It would be nice to be able to test that, and get coverage of the methods for `GUnixMountPoint` and `GUnixMountEntry`. --- gio/tests/unix-mounts.c | 283 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 283 insertions(+) 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 (); } From 93fb9951aade603e49c357e0fc9db933cc4dea20 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Sat, 20 Jul 2024 17:32:53 +0200 Subject: [PATCH 3/3] gunixmounts: Drop some Interix and QNX support This can never have been tested, it was returning `GUnixMountEntry` structs from functions which are typed to return `GUnixMountPoint`s. Signed-off-by: Philip Withnall --- gio/gunixmounts.c | 41 ----------------------------------------- 1 file changed, 41 deletions(-) diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c index 8e410c90c..96733d9cc 100644 --- a/gio/gunixmounts.c +++ b/gio/gunixmounts.c @@ -1752,47 +1752,6 @@ _g_unix_mount_points_get_from_file (const char *table_path, *n_points_out = 0; return NULL; } -/* Interix {{{2 */ -#elif defined(__INTERIX) -static GList * -_g_get_unix_mount_points (void) -{ - return _g_get_unix_mounts (); -} - -static GUnixMountPoint ** -_g_unix_mount_points_get_from_file (const char *table_path, - uint64_t *time_read_out, - size_t *n_points_out) -{ - /* Not supported on Interix systems. */ - if (time_read_out != NULL) - *time_read_out = 0; - if (n_points_out != NULL) - *n_points_out = 0; - return NULL; -} - -/* QNX {{{2 */ -#elif defined (HAVE_QNX) -static GList * -_g_get_unix_mount_points (void) -{ - return _g_get_unix_mounts (); -} - -static GUnixMountPoint ** -_g_unix_mount_points_get_from_file (const char *table_path, - uint64_t *time_read_out, - size_t *n_points_out) -{ - /* Not supported on QNX systems. */ - if (time_read_out != NULL) - *time_read_out = 0; - if (n_points_out != NULL) - *n_points_out = 0; - return NULL; -} /* Common code {{{2 */ #else