Merge branch 'large-file-issue3' into 'main'

gfileutils: Fix g_file_get_contents() silent under-read of large files when off_t is wider than size_t

See merge request GNOME/glib!3713
This commit is contained in:
Philip Withnall 2023-11-23 12:41:42 +00:00
commit 9c9552309a
2 changed files with 136 additions and 8 deletions

View File

@ -750,8 +750,8 @@ get_contents_stdio (const gchar *filename,
g_set_error (error, g_set_error (error,
G_FILE_ERROR, G_FILE_ERROR,
G_FILE_ERROR_NOMEM, G_FILE_ERROR_NOMEM,
g_dngettext (GETTEXT_PACKAGE, "Could not allocate %lu byte to read file “%s”", "Could not allocate %lu bytes to read file “%s”", (gulong)total_allocated), g_dngettext (GETTEXT_PACKAGE, "Could not allocate %" G_GSIZE_MODIFIER "u byte to read file “%s”", "Could not allocate %" G_GSIZE_MODIFIER "u bytes to read file “%s”", total_allocated),
(gulong) total_allocated, total_allocated,
display_filename); display_filename);
g_free (display_filename); g_free (display_filename);
@ -831,6 +831,18 @@ get_contents_regfile (const gchar *filename,
gsize alloc_size; gsize alloc_size;
gchar *display_filename; gchar *display_filename;
if ((G_MAXOFFSET >= G_MAXSIZE) && (stat_buf->st_size > (goffset) (G_MAXSIZE - 1)))
{
display_filename = g_filename_display_name (filename);
g_set_error (error,
G_FILE_ERROR,
G_FILE_ERROR_FAILED,
_("File “%s” is too large"),
display_filename);
g_free (display_filename);
goto error;
}
size = stat_buf->st_size; size = stat_buf->st_size;
alloc_size = size + 1; alloc_size = size + 1;
@ -842,8 +854,8 @@ get_contents_regfile (const gchar *filename,
g_set_error (error, g_set_error (error,
G_FILE_ERROR, G_FILE_ERROR,
G_FILE_ERROR_NOMEM, G_FILE_ERROR_NOMEM,
g_dngettext (GETTEXT_PACKAGE, "Could not allocate %lu byte to read file “%s”", "Could not allocate %lu bytes to read file “%s”", (gulong)alloc_size), g_dngettext (GETTEXT_PACKAGE, "Could not allocate %" G_GSIZE_MODIFIER "u byte to read file “%s”", "Could not allocate %" G_GSIZE_MODIFIER "u bytes to read file “%s”", alloc_size),
(gulong) alloc_size, alloc_size,
display_filename); display_filename);
g_free (display_filename); g_free (display_filename);
goto error; goto error;
@ -891,7 +903,7 @@ get_contents_regfile (const gchar *filename,
return TRUE; return TRUE;
error: error:
close (fd); close (fd);

View File

@ -1474,6 +1474,121 @@ test_get_contents (void)
g_remove (filename); g_remove (filename);
} }
static gboolean
resize_file (const gchar *filename,
gint64 size)
{
int fd;
int retval;
fd = g_open (filename, O_CREAT | O_RDWR | O_TRUNC, 0666);
g_assert_cmpint (fd, >=, 0);
#ifdef G_OS_WIN32
retval = _chsize_s (fd, size);
#else
retval = ftruncate64 (fd, size);
#endif
if (retval != 0)
{
g_test_message ("Error trying to resize file (%s)", strerror (errno));
close (fd);
return FALSE;
}
close (fd);
return TRUE;
}
static gboolean
is_error_in_list (GFileError error_code,
const GFileError ok_list[],
size_t ok_count)
{
for (size_t i = 0; i < ok_count; i++)
{
if (ok_list[i] == error_code)
return TRUE;
}
return FALSE;
}
static void
get_largefile_check_len (const gchar *filename,
gint64 large_len,
const GFileError ok_list[],
size_t ok_count)
{
gboolean get_ok;
gsize len;
gchar *contents;
GError *error = NULL;
get_ok = g_file_get_contents (filename, &contents, &len, &error);
if (get_ok)
{
g_assert_cmpint ((gint64) len, ==, large_len);
g_free (contents);
}
else
{
g_assert_cmpint (error->domain, ==, G_FILE_ERROR);
if (is_error_in_list ((GFileError)error->code, ok_list, ok_count))
{
g_test_message ("Error reading file of size 0x%" G_GINT64_MODIFIER "x, but with acceptable error type (%s)", large_len, error->message);
}
else
{
/* fail for other errors */
g_assert_no_error (error);
}
g_clear_error (&error);
}
}
static void
test_get_contents_largefile (void)
{
if (!g_test_slow ())
{
g_test_skip ("Skipping slow largefile test");
return;
}
const gchar *filename = "file-test-get-contents-large";
gint64 large_len;
/* error OK if couldn't allocate large buffer, or if file is too large */
const GFileError too_large_errors[] = { G_FILE_ERROR_NOMEM, G_FILE_ERROR_FAILED };
/* error OK if couldn't allocate large buffer */
const GFileError nomem_errors[] = { G_FILE_ERROR_NOMEM };
/* OK to fail to read this, but don't silently under-read */
large_len = (G_GINT64_CONSTANT (1) << 32) + 16;
if (!resize_file (filename, large_len))
goto failed_resize;
get_largefile_check_len (filename, large_len, too_large_errors, G_N_ELEMENTS (too_large_errors));
/* OK to fail to read this size, but don't silently under-read */
large_len = (G_GINT64_CONSTANT (1) << 32) - 1;
if (!resize_file (filename, large_len))
goto failed_resize;
get_largefile_check_len (filename, large_len, too_large_errors, G_N_ELEMENTS (too_large_errors));
/* OK to fail memory allocation, but don't otherwise fail this size */
large_len = (G_GINT64_CONSTANT (1) << 31) - 1;
if (!resize_file (filename, large_len))
goto failed_resize;
get_largefile_check_len (filename, large_len, nomem_errors, G_N_ELEMENTS (nomem_errors));
g_remove (filename);
return;
failed_resize:
g_test_incomplete ("Failed to resize large file, unable to complete large file tests.");
g_remove (filename);
}
static void static void
test_file_test (void) test_file_test (void)
{ {
@ -2648,6 +2763,7 @@ main (int argc,
g_test_add_func ("/fileutils/mkstemp", test_mkstemp); g_test_add_func ("/fileutils/mkstemp", test_mkstemp);
g_test_add_func ("/fileutils/mkdtemp", test_mkdtemp); g_test_add_func ("/fileutils/mkdtemp", test_mkdtemp);
g_test_add_func ("/fileutils/get-contents", test_get_contents); g_test_add_func ("/fileutils/get-contents", test_get_contents);
g_test_add_func ("/fileutils/get-contents-large-file", test_get_contents_largefile);
g_test_add_func ("/fileutils/set-contents", test_set_contents); g_test_add_func ("/fileutils/set-contents", test_set_contents);
g_test_add_func ("/fileutils/set-contents-full", test_set_contents_full); g_test_add_func ("/fileutils/set-contents-full", test_set_contents_full);
g_test_add_func ("/fileutils/set-contents-full/read-only-file", test_set_contents_full_read_only_file); g_test_add_func ("/fileutils/set-contents-full/read-only-file", test_set_contents_full_read_only_file);