mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-11-04 10:08:56 +01:00 
			
		
		
		
	glocalfileoutputstream: Fix error handling for a corner case
If asked to replace a file (i.e. effectively delete and re-create it) which is a symlink, in a read-only directory, the code would end up trying to do operations on an invalid `fd`. This was masked when asked to create a backup, as creating the backup in the read-only directory would error out just in time. Make the code a bit more robust in this situation, and add some unit tests. Spotted by scan-build. Signed-off-by: Philip Withnall <pwithnall@gnome.org>
This commit is contained in:
		@@ -1162,6 +1162,12 @@ handle_overwrite_open (const char    *filename,
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
      /* The FD could be invalid if the file is a symlink, a backup has been
 | 
			
		||||
       * requested, and the source directory is read-only (as we won’t have
 | 
			
		||||
       * errored out earlier), but in that case creating the backup file will
 | 
			
		||||
       * have failed just above. */
 | 
			
		||||
      g_assert (fd >= 0);
 | 
			
		||||
 | 
			
		||||
      if (!copy_file_data (fd, bfd, NULL))
 | 
			
		||||
	{
 | 
			
		||||
	  g_set_error_literal (error,
 | 
			
		||||
@@ -1193,8 +1199,7 @@ handle_overwrite_open (const char    *filename,
 | 
			
		||||
 | 
			
		||||
  if (replace_destination_set)
 | 
			
		||||
    {
 | 
			
		||||
      g_close (fd, NULL);
 | 
			
		||||
      fd = -1;
 | 
			
		||||
      g_clear_fd (&fd, NULL);
 | 
			
		||||
 | 
			
		||||
      if (g_unlink (filename) != 0)
 | 
			
		||||
	{
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										114
									
								
								gio/tests/file.c
									
									
									
									
									
								
							
							
						
						
									
										114
									
								
								gio/tests/file.c
									
									
									
									
									
								
							@@ -1092,6 +1092,13 @@ test_replace_symlink_using_etag (void)
 | 
			
		||||
 * See https://gitlab.gnome.org/GNOME/glib/-/issues/2325 */
 | 
			
		||||
#ifdef __linux__
 | 
			
		||||
 | 
			
		||||
/* Different arrangements of parent tmp directory. */
 | 
			
		||||
typedef enum
 | 
			
		||||
{
 | 
			
		||||
  FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
  FILE_TEST_DIRECTORY_SETUP_TYPE_READ_ONLY,
 | 
			
		||||
} FileTestDirectorySetupType;
 | 
			
		||||
 | 
			
		||||
/* Different kinds of file which create_test_file() can create. */
 | 
			
		||||
typedef enum
 | 
			
		||||
{
 | 
			
		||||
@@ -1420,6 +1427,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      const gchar *replace_etag;  /* (nullable) */
 | 
			
		||||
 | 
			
		||||
      /* File system setup. */
 | 
			
		||||
      FileTestDirectorySetupType setup_directory_type;
 | 
			
		||||
      FileTestSetupType setup_source_type;
 | 
			
		||||
      guint setup_source_mode;
 | 
			
		||||
      FileTestSetupType setup_backup_type;
 | 
			
		||||
@@ -1447,6 +1455,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
       * file created to check it’s not modified */
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1455,6 +1464,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1463,6 +1473,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1471,6 +1482,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_DIRECTORY, 0,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        FALSE, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
 | 
			
		||||
@@ -1479,6 +1491,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        FALSE, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
 | 
			
		||||
@@ -1487,6 +1500,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1495,6 +1509,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1506,6 +1521,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
       * regular non-empty file; replacement should fail */
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_NONE, "incorrect etag",
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        FALSE, G_IO_ERROR, G_IO_ERROR_WRONG_ETAG,
 | 
			
		||||
@@ -1518,6 +1534,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
       * file created to check it’s either replaced or the operation fails */
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1526,6 +1543,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1534,6 +1552,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1542,6 +1561,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_DIRECTORY, 0,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        FALSE, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
 | 
			
		||||
@@ -1550,6 +1570,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        FALSE, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
 | 
			
		||||
@@ -1558,6 +1579,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1574,6 +1596,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1590,6 +1613,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
       * created to check it’s either replaced or the operation fails */
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1598,6 +1622,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1606,6 +1631,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1614,6 +1640,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_DIRECTORY, 0, FALSE,
 | 
			
		||||
        FALSE, G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP,
 | 
			
		||||
@@ -1622,6 +1649,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1630,6 +1658,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1638,6 +1667,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1652,6 +1682,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
       * mostly with a backup file created to check it’s not modified */
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1660,6 +1691,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1668,6 +1700,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1676,6 +1709,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_DIRECTORY, 0,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        FALSE, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
 | 
			
		||||
@@ -1684,6 +1718,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        FALSE, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
 | 
			
		||||
@@ -1692,6 +1727,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1700,6 +1736,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1714,6 +1751,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
       * should fail */
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_REPLACE_DESTINATION, "incorrect etag",
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        FALSE, G_IO_ERROR, G_IO_ERROR_WRONG_ETAG,
 | 
			
		||||
@@ -1727,6 +1765,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
       * operation fails */
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1735,6 +1774,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1743,6 +1783,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1751,6 +1792,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_DIRECTORY, 0,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        FALSE, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
 | 
			
		||||
@@ -1759,6 +1801,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        FALSE, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE,
 | 
			
		||||
@@ -1767,6 +1810,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1775,6 +1819,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1790,6 +1835,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
       * operation fails */
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1798,6 +1844,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1806,6 +1853,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1814,6 +1862,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_DIRECTORY, 0, FALSE,
 | 
			
		||||
        FALSE, G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP,
 | 
			
		||||
@@ -1822,6 +1871,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SOCKET, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1830,6 +1880,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SYMLINK_DANGLING, 0, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1838,6 +1889,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1850,6 +1902,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      /* several different setups with replace_flags == PRIVATE */
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_PRIVATE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1858,6 +1911,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_PRIVATE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1867,6 +1921,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_PRIVATE | G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1875,6 +1930,7 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_PRIVATE | G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_NONEMPTY, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, FALSE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
@@ -1889,12 +1945,53 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
       * and in particular if we're root. In this scenario,we need to skip it */
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_NORMAL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, 0  /* most restrictive permissions */,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, TRUE,
 | 
			
		||||
        FALSE, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
 | 
			
		||||
        1, FILE_TEST_SETUP_TYPE_REGULAR_EMPTY, 0, NULL,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      /* A symlink in an unwriteable directory, with the need to replace the
 | 
			
		||||
       * destination and make a backup, tests the fallback backup code path. It
 | 
			
		||||
       * can’t succeed, because even if the file replace would work, the backup
 | 
			
		||||
       * file cannot be created in a read-only directory. */
 | 
			
		||||
      {
 | 
			
		||||
        TRUE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_READ_ONLY,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, TRUE,
 | 
			
		||||
        FALSE, G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP,
 | 
			
		||||
        2, FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, "source-target",
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      /* Same as above, but without trying to create a backup. We expect this to
 | 
			
		||||
       * fail because replacing the destination requires deleting and
 | 
			
		||||
       * re-creating the file, which can’t happen in a read-only directory. */
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_READ_ONLY,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, TRUE,
 | 
			
		||||
        FALSE, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
 | 
			
		||||
        2, FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, "source-target",
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      /* Same as above, but without trying to create a backup or trying to
 | 
			
		||||
       * replace the file. */
 | 
			
		||||
      {
 | 
			
		||||
        FALSE, G_FILE_CREATE_NONE, NULL,
 | 
			
		||||
        FILE_TEST_DIRECTORY_SETUP_TYPE_READ_ONLY,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode,
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, TRUE,
 | 
			
		||||
        TRUE, 0, 0,
 | 
			
		||||
        /* the second file is the source-target file, which should contain the new_contents */
 | 
			
		||||
        2, FILE_TEST_SETUP_TYPE_SYMLINK_VALID, default_public_mode, "source-target",
 | 
			
		||||
        FILE_TEST_SETUP_TYPE_NONEXISTENT, 0, NULL,
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
  gsize i;
 | 
			
		||||
 | 
			
		||||
@@ -1941,6 +2038,15 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
      source_file = create_test_file (tmpdir, "source", tests[i].setup_source_type, tests[i].setup_source_mode);
 | 
			
		||||
      backup_file = create_test_file (tmpdir, "source~", tests[i].setup_backup_type, tests[i].setup_backup_mode);
 | 
			
		||||
 | 
			
		||||
      /* Make the tmpdir read-only if desired. */
 | 
			
		||||
      if (tests[i].setup_directory_type == FILE_TEST_DIRECTORY_SETUP_TYPE_READ_ONLY)
 | 
			
		||||
        {
 | 
			
		||||
          g_file_set_attribute_uint32 (tmpdir, G_FILE_ATTRIBUTE_UNIX_MODE,
 | 
			
		||||
                                       0500, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
 | 
			
		||||
                                       NULL, &local_error);
 | 
			
		||||
          g_assert_no_error (local_error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      /* Replace the source file. Check the error state only after finishing
 | 
			
		||||
       * writing, as the replace operation is split across g_file_replace() and
 | 
			
		||||
       * g_output_stream_close(). */
 | 
			
		||||
@@ -2042,6 +2148,14 @@ test_replace (gconstpointer test_data)
 | 
			
		||||
 | 
			
		||||
      /* Tidy up. Ignore failure apart from when deleting the directory, which
 | 
			
		||||
       * should be empty. */
 | 
			
		||||
      if (tests[i].setup_directory_type == FILE_TEST_DIRECTORY_SETUP_TYPE_READ_ONLY)
 | 
			
		||||
        {
 | 
			
		||||
          g_file_set_attribute_uint32 (tmpdir, G_FILE_ATTRIBUTE_UNIX_MODE,
 | 
			
		||||
                                       0700, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
 | 
			
		||||
                                       NULL, &local_error);
 | 
			
		||||
          g_assert_no_error (local_error);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
      g_file_delete (source_file, NULL, NULL);
 | 
			
		||||
      g_file_delete (backup_file, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user