From 0c5f084200bd9667d1c73b03e9e0482cc2384a93 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Fri, 1 Jun 2018 15:43:27 +0100 Subject: [PATCH] fileinfo: make UNIX_IS_MOUNTPOINT be TRUE for / The previously implementation considered a file to be a mountpoint if its parent is on a different device. / is its own parent, so by this definition it is not a mountpoint. But / is (generally) listed in fstab, and fstab(5) defines the directories it contains to be mountpoints. This attribute should follow that definition (and reasonable expectation): the root directory is a mountpoint. So, add a special-case for the case where the file's parent has the same st_dev and st_ino as the file, which is true only at the root. Test this attribute at / (only on POSIX), /proc (but only on Linux), and at many files and directories created by the test suite (which cannot be mountpoints). --- gio/gfileinfo.h | 3 ++- gio/glocalfileinfo.c | 4 +++- gio/glocalfileinfo.h | 1 + gio/tests/live-g-file.c | 48 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/gio/gfileinfo.h b/gio/gfileinfo.h index 4decee346..8416b4fad 100644 --- a/gio/gfileinfo.h +++ b/gio/gfileinfo.h @@ -644,7 +644,8 @@ typedef struct _GFileInfoClass GFileInfoClass; * * A key in the "unix" namespace for checking if the file represents a * UNIX mount point. This attribute is %TRUE if the file is a UNIX mount - * point. This attribute is only available for UNIX file systems. + * point. Since 2.58, `/` is considered to be a mount point. + * This attribute is only available for UNIX file systems. * Corresponding #GFileAttributeType is %G_FILE_ATTRIBUTE_TYPE_BOOLEAN. **/ #define G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT "unix::is-mountpoint" /* boolean */ diff --git a/gio/glocalfileinfo.c b/gio/glocalfileinfo.c index 86bbb225b..df0352a8a 100644 --- a/gio/glocalfileinfo.c +++ b/gio/glocalfileinfo.c @@ -821,6 +821,7 @@ _g_local_file_info_get_parent_info (const char *dir, parent_info->is_sticky = FALSE; parent_info->has_trash_dir = FALSE; parent_info->device = 0; + parent_info->inode = 0; if (_g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_RENAME) || _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_DELETE) || @@ -850,6 +851,7 @@ _g_local_file_info_get_parent_info (const char *dir, #endif parent_info->owner = statbuf.st_uid; parent_info->device = statbuf.st_dev; + parent_info->inode = statbuf.st_ino; /* No need to find trash dir if it's not writable anyway */ if (parent_info->writable && _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_ACCESS_CAN_TRASH)) @@ -1965,7 +1967,7 @@ _g_local_file_info_get (const char *basename, if (stat_ok && parent_info && parent_info->device != 0 && _g_file_attribute_matcher_matches_id (attribute_matcher, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT) && - statbuf.st_dev != parent_info->device) + (statbuf.st_dev != parent_info->device || statbuf.st_ino == parent_info->inode)) _g_file_info_set_attribute_boolean_by_id (info, G_FILE_ATTRIBUTE_ID_UNIX_IS_MOUNTPOINT, TRUE); if (stat_ok) diff --git a/gio/glocalfileinfo.h b/gio/glocalfileinfo.h index a231c24ca..7a6566163 100644 --- a/gio/glocalfileinfo.h +++ b/gio/glocalfileinfo.h @@ -36,6 +36,7 @@ typedef struct gboolean has_trash_dir; int owner; dev_t device; + ino_t inode; gpointer extra_data; GDestroyNotify free_extra_data; } GLocalParentFileInfo; diff --git a/gio/tests/live-g-file.c b/gio/tests/live-g-file.c index ba9d8d230..240fa8b25 100644 --- a/gio/tests/live-g-file.c +++ b/gio/tests/live-g-file.c @@ -417,6 +417,15 @@ test_attributes (struct StructureItem item, GFileInfo * info) G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN); g_assert_cmpint (is_hidden, ==, TRUE); } + + /* unix::is-mountpoint */ + if (posix_compat) + { + gboolean is_mountpoint = + g_file_info_get_attribute_boolean (info, + G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT); + g_assert_false (is_mountpoint); + } } static void @@ -853,6 +862,33 @@ test_copy_move (gconstpointer test_data) g_object_unref (root); } +/* Test that G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT is TRUE for / and for another + * known mountpoint. The FALSE case is tested for many directories and files by + * test_initial_structure(), via test_attributes(). + */ +static void +test_unix_is_mountpoint (gconstpointer data) +{ + const gchar *path = data; + GFile *file = g_file_new_for_path (path); + GFileInfo *info; + gboolean is_mountpoint; + GError *error = NULL; + + info = g_file_query_info (file, G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT, + G_FILE_QUERY_INFO_NONE, NULL, &error); + g_assert_no_error (error); + g_assert_nonnull (info); + + is_mountpoint = + g_file_info_get_attribute_boolean (info, + G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT); + g_assert_true (is_mountpoint); + + g_clear_object (&info); + g_clear_object (&file); +} + static void test_create (gconstpointer test_data) { @@ -1342,6 +1378,18 @@ main (int argc, char *argv[]) if (!only_create_struct) g_test_add_data_func ("/live-g-file/test_open", target_path, test_open); + if (posix_compat) + { + g_test_add_data_func ("/live-g-file/test_unix_is_mountpoint/sysroot", + "/", + test_unix_is_mountpoint); +#ifdef __linux__ + g_test_add_data_func ("/live-g-file/test_unix_is_mountpoint/proc", + "/proc", + test_unix_is_mountpoint); +#endif + } + /* Write test - create */ if (write_test && (!only_create_struct)) g_test_add_data_func ("/live-g-file/test_create", target_path,