mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-14 13:26:16 +01:00
b9d04b37b0
Some CI platforms invoke these tests with euid != 0 but with capabilities. Detect whether we have Linux CAP_DAC_OVERRIDE or other OSs' equivalents, and skip tests that rely on DAC permissions being denied if we do have that privilege. Signed-off-by: Simon McVittie <smcv@collabora.com> Fixes: https://gitlab.gnome.org/GNOME/glib/issues/2027 Fixes: https://gitlab.gnome.org/GNOME/glib/issues/2028
1494 lines
43 KiB
C
1494 lines
43 KiB
C
/* GLib testing framework examples and tests
|
|
* Copyright (C) 2008 Red Hat, Inc.
|
|
* Authors: Tomas Bzatek <tbzatek@redhat.com>
|
|
*
|
|
* This work is provided "as is"; redistribution and modification
|
|
* in whole or in part, in any medium, physical or electronic is
|
|
* permitted without restriction.
|
|
*
|
|
* This work is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*
|
|
* In no event shall the authors or contributors be liable for any
|
|
* direct, indirect, incidental, special, exemplary, or consequential
|
|
* damages (including, but not limited to, procurement of substitute
|
|
* goods or services; loss of use, data, or profits; or business
|
|
* interruption) however caused and on any theory of liability, whether
|
|
* in contract, strict liability, or tort (including negligence or
|
|
* otherwise) arising in any way out of the use of this software, even
|
|
* if advised of the possibility of such damage.
|
|
*/
|
|
|
|
#include <glib/glib.h>
|
|
#include <gio/gio.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
|
|
#define DEFAULT_TEST_DIR "testdir_live-g-file"
|
|
|
|
#define PATTERN_FILE_SIZE 0x10000
|
|
#define TEST_HANDLE_SPECIAL TRUE
|
|
|
|
enum StructureExtraFlags
|
|
{
|
|
TEST_DELETE_NORMAL = 1 << 0,
|
|
TEST_DELETE_TRASH = 1 << 1,
|
|
TEST_DELETE_NON_EMPTY = 1 << 2,
|
|
TEST_DELETE_FAILURE = 1 << 3,
|
|
TEST_NOT_EXISTS = 1 << 4,
|
|
TEST_ENUMERATE_FILE = 1 << 5,
|
|
TEST_NO_ACCESS = 1 << 6,
|
|
TEST_COPY = 1 << 7,
|
|
TEST_MOVE = 1 << 8,
|
|
TEST_COPY_ERROR_RECURSE = 1 << 9,
|
|
TEST_ALREADY_EXISTS = 1 << 10,
|
|
TEST_TARGET_IS_FILE = 1 << 11,
|
|
TEST_CREATE = 1 << 12,
|
|
TEST_REPLACE = 1 << 13,
|
|
TEST_APPEND = 1 << 14,
|
|
TEST_OPEN = 1 << 15,
|
|
TEST_OVERWRITE = 1 << 16,
|
|
TEST_INVALID_SYMLINK = 1 << 17,
|
|
TEST_HIDDEN = 1 << 18,
|
|
TEST_DOT_HIDDEN = 1 << 19,
|
|
};
|
|
|
|
struct StructureItem
|
|
{
|
|
const char *filename;
|
|
const char *link_to;
|
|
GFileType file_type;
|
|
GFileCreateFlags create_flags;
|
|
guint32 mode;
|
|
gboolean handle_special;
|
|
enum StructureExtraFlags extra_flags;
|
|
};
|
|
|
|
#define TEST_DIR_NO_ACCESS "dir_no-access"
|
|
#define TEST_DIR_NO_WRITE "dir_no-write"
|
|
#define TEST_DIR_TARGET "dir-target"
|
|
#define TEST_NAME_NOT_EXISTS "not_exists"
|
|
#define TEST_TARGET_FILE "target-file"
|
|
|
|
|
|
static const struct StructureItem sample_struct[] = {
|
|
/* filename link file_type create_flags mode | handle_special | extra_flags */
|
|
{"dir1", NULL, G_FILE_TYPE_DIRECTORY, G_FILE_CREATE_NONE, 0, 0, TEST_DELETE_NORMAL | TEST_DELETE_NON_EMPTY | TEST_REPLACE | TEST_OPEN},
|
|
{"dir1/subdir", NULL, G_FILE_TYPE_DIRECTORY, G_FILE_CREATE_NONE, 0, 0, TEST_COPY | TEST_COPY_ERROR_RECURSE | TEST_APPEND},
|
|
{"dir2", NULL, G_FILE_TYPE_DIRECTORY, G_FILE_CREATE_NONE, 0, 0, TEST_DELETE_NORMAL | TEST_MOVE | TEST_CREATE},
|
|
{TEST_DIR_TARGET, NULL, G_FILE_TYPE_DIRECTORY, G_FILE_CREATE_NONE, 0, 0, TEST_COPY | TEST_COPY_ERROR_RECURSE},
|
|
{TEST_DIR_NO_ACCESS, NULL, G_FILE_TYPE_DIRECTORY, G_FILE_CREATE_PRIVATE, S_IRUSR + S_IWUSR + S_IRGRP + S_IWGRP + S_IROTH + S_IWOTH, 0, TEST_NO_ACCESS | TEST_OPEN},
|
|
{TEST_DIR_NO_WRITE, NULL, G_FILE_TYPE_DIRECTORY, G_FILE_CREATE_PRIVATE, S_IRUSR + S_IXUSR + S_IRGRP + S_IXGRP + S_IROTH + S_IXOTH, 0, 0},
|
|
{TEST_TARGET_FILE, NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, 0, TEST_COPY | TEST_OPEN},
|
|
{"normal_file", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, 0, TEST_ENUMERATE_FILE | TEST_CREATE | TEST_OVERWRITE},
|
|
{"normal_file-symlink", "normal_file", G_FILE_TYPE_SYMBOLIC_LINK, G_FILE_CREATE_NONE, 0, 0, TEST_ENUMERATE_FILE | TEST_COPY | TEST_OPEN},
|
|
{"executable_file", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, S_IRWXU + S_IRWXG + S_IRWXO, 0, TEST_DELETE_TRASH | TEST_COPY | TEST_OPEN | TEST_OVERWRITE | TEST_REPLACE},
|
|
{"private_file", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_PRIVATE, 0, 0, TEST_COPY | TEST_OPEN | TEST_OVERWRITE | TEST_APPEND},
|
|
{"normal_file2", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, 0, TEST_COPY | TEST_OVERWRITE | TEST_REPLACE},
|
|
{"readonly_file", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, S_IRUSR + S_IRGRP + S_IROTH, 0, TEST_DELETE_NORMAL | TEST_OPEN},
|
|
{"UTF_pr\xcc\x8ci\xcc\x81lis\xcc\x8c z",
|
|
NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, 0, TEST_COPY | TEST_CREATE | TEST_OPEN | TEST_OVERWRITE},
|
|
{"dir_pr\xcc\x8ci\xcc\x81lis\xcc\x8c z",
|
|
NULL, G_FILE_TYPE_DIRECTORY, G_FILE_CREATE_NONE, 0, 0, TEST_DELETE_NORMAL | TEST_CREATE},
|
|
{"pattern_file", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, TEST_HANDLE_SPECIAL, TEST_COPY | TEST_OPEN | TEST_APPEND},
|
|
{TEST_NAME_NOT_EXISTS, NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, TEST_HANDLE_SPECIAL, TEST_DELETE_NORMAL | TEST_NOT_EXISTS | TEST_COPY | TEST_OPEN},
|
|
{TEST_NAME_NOT_EXISTS, NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, TEST_HANDLE_SPECIAL, TEST_DELETE_TRASH | TEST_NOT_EXISTS | TEST_MOVE},
|
|
{"not_exists2", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, TEST_HANDLE_SPECIAL, TEST_NOT_EXISTS | TEST_CREATE},
|
|
{"not_exists3", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, TEST_HANDLE_SPECIAL, TEST_NOT_EXISTS | TEST_REPLACE},
|
|
{"not_exists4", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, TEST_HANDLE_SPECIAL, TEST_NOT_EXISTS | TEST_APPEND},
|
|
{"dir_no-execute/file", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, TEST_HANDLE_SPECIAL, TEST_DELETE_NORMAL | TEST_DELETE_FAILURE | TEST_NOT_EXISTS | TEST_OPEN},
|
|
{"lost_symlink", "nowhere", G_FILE_TYPE_SYMBOLIC_LINK, G_FILE_CREATE_NONE, 0, 0, TEST_COPY | TEST_DELETE_NORMAL | TEST_OPEN | TEST_INVALID_SYMLINK},
|
|
{"dir_hidden", NULL, G_FILE_TYPE_DIRECTORY, G_FILE_CREATE_NONE, 0, 0, 0},
|
|
{"dir_hidden/.hidden", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, TEST_HANDLE_SPECIAL, 0},
|
|
{"dir_hidden/.a-hidden-file", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, 0, TEST_HIDDEN},
|
|
{"dir_hidden/file-in-.hidden1", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, 0, TEST_HIDDEN | TEST_DOT_HIDDEN},
|
|
{"dir_hidden/file-in-.hidden2", NULL, G_FILE_TYPE_REGULAR, G_FILE_CREATE_NONE, 0, 0, TEST_HIDDEN | TEST_DOT_HIDDEN},
|
|
};
|
|
|
|
static gboolean test_suite;
|
|
static gboolean write_test;
|
|
static gboolean verbose;
|
|
static gboolean posix_compat;
|
|
|
|
#ifdef G_OS_UNIX
|
|
/*
|
|
* check_cap_dac_override:
|
|
* @tmpdir: A temporary directory in which we can create and delete files
|
|
*
|
|
* Check whether the current process can bypass DAC permissions.
|
|
*
|
|
* Traditionally, "privileged" processes (those with effective uid 0)
|
|
* could do this (and bypass many other checks), and "unprivileged"
|
|
* processes could not.
|
|
*
|
|
* In Linux, the special powers of euid 0 are divided into many
|
|
* capabilities: see `capabilities(7)`. The one we are interested in
|
|
* here is `CAP_DAC_OVERRIDE`.
|
|
*
|
|
* We do this generically instead of actually looking at the capability
|
|
* bits, so that the right thing will happen on non-Linux Unix
|
|
* implementations, in particular if they have something equivalent to
|
|
* but not identical to Linux permissions.
|
|
*
|
|
* Returns: %TRUE if we have Linux `CAP_DAC_OVERRIDE` or equivalent
|
|
* privileges
|
|
*/
|
|
static gboolean
|
|
check_cap_dac_override (const char *tmpdir)
|
|
{
|
|
gchar *dac_denies_write;
|
|
gchar *inside;
|
|
gboolean have_cap;
|
|
|
|
dac_denies_write = g_build_filename (tmpdir, "dac-denies-write", NULL);
|
|
inside = g_build_filename (dac_denies_write, "inside", NULL);
|
|
|
|
g_assert_cmpint (mkdir (dac_denies_write, S_IRWXU) == 0 ? 0 : errno, ==, 0);
|
|
g_assert_cmpint (chmod (dac_denies_write, 0) == 0 ? 0 : errno, ==, 0);
|
|
|
|
if (mkdir (inside, S_IRWXU) == 0)
|
|
{
|
|
g_test_message ("Looks like we have CAP_DAC_OVERRIDE or equivalent");
|
|
g_assert_cmpint (rmdir (inside) == 0 ? 0 : errno, ==, 0);
|
|
have_cap = TRUE;
|
|
}
|
|
else
|
|
{
|
|
int saved_errno = errno;
|
|
|
|
g_test_message ("We do not have CAP_DAC_OVERRIDE or equivalent");
|
|
g_assert_cmpint (saved_errno, ==, EACCES);
|
|
have_cap = FALSE;
|
|
}
|
|
|
|
g_assert_cmpint (chmod (dac_denies_write, S_IRWXU) == 0 ? 0 : errno, ==, 0);
|
|
g_assert_cmpint (rmdir (dac_denies_write) == 0 ? 0 : errno, ==, 0);
|
|
g_free (dac_denies_write);
|
|
g_free (inside);
|
|
return have_cap;
|
|
}
|
|
#endif
|
|
|
|
#ifdef G_HAVE_ISO_VARARGS
|
|
#define log(...) if (verbose) g_printerr (__VA_ARGS__)
|
|
#elif defined(G_HAVE_GNUC_VARARGS)
|
|
#define log(msg...) if (verbose) g_printerr (msg)
|
|
#else /* no varargs macros */
|
|
static void log (const g_char *format, ...)
|
|
{
|
|
va_list args;
|
|
va_start (args, format);
|
|
if (verbose) g_printerr (format, args);
|
|
va_end (args);
|
|
}
|
|
#endif
|
|
|
|
static GFile *
|
|
create_empty_file (GFile * parent, const char *filename,
|
|
GFileCreateFlags create_flags)
|
|
{
|
|
GFile *child;
|
|
GError *error;
|
|
GFileOutputStream *outs;
|
|
|
|
child = g_file_get_child (parent, filename);
|
|
g_assert_nonnull (child);
|
|
|
|
error = NULL;
|
|
outs = g_file_replace (child, NULL, FALSE, create_flags, NULL, &error);
|
|
g_assert_no_error (error);
|
|
g_assert_nonnull (outs);
|
|
error = NULL;
|
|
g_output_stream_close (G_OUTPUT_STREAM (outs), NULL, &error);
|
|
g_object_unref (outs);
|
|
return child;
|
|
}
|
|
|
|
static GFile *
|
|
create_empty_dir (GFile * parent, const char *filename)
|
|
{
|
|
GFile *child;
|
|
gboolean res;
|
|
GError *error;
|
|
|
|
child = g_file_get_child (parent, filename);
|
|
g_assert_nonnull (child);
|
|
error = NULL;
|
|
res = g_file_make_directory (child, NULL, &error);
|
|
g_assert_true (res);
|
|
g_assert_no_error (error);
|
|
return child;
|
|
}
|
|
|
|
static GFile *
|
|
create_symlink (GFile * parent, const char *filename, const char *points_to)
|
|
{
|
|
GFile *child;
|
|
gboolean res;
|
|
GError *error;
|
|
|
|
child = g_file_get_child (parent, filename);
|
|
g_assert_nonnull (child);
|
|
error = NULL;
|
|
res = g_file_make_symbolic_link (child, points_to, NULL, &error);
|
|
g_assert_true (res);
|
|
g_assert_no_error (error);
|
|
return child;
|
|
}
|
|
|
|
static void
|
|
test_create_structure (gconstpointer test_data)
|
|
{
|
|
GFile *root;
|
|
GFile *child;
|
|
gboolean res;
|
|
GError *error = NULL;
|
|
GFileOutputStream *outs;
|
|
GDataOutputStream *outds;
|
|
guint i;
|
|
struct StructureItem item;
|
|
|
|
g_assert_nonnull (test_data);
|
|
log ("\n Going to create testing structure in '%s'...\n",
|
|
(char *) test_data);
|
|
|
|
root = g_file_new_for_commandline_arg ((char *) test_data);
|
|
g_assert_nonnull (root);
|
|
|
|
/* create root directory */
|
|
g_file_make_directory (root, NULL, &error);
|
|
g_assert_no_error (error);
|
|
|
|
/* create any other items */
|
|
for (i = 0; i < G_N_ELEMENTS (sample_struct); i++)
|
|
{
|
|
item = sample_struct[i];
|
|
if ((item.handle_special)
|
|
|| ((!posix_compat)
|
|
&& (item.file_type == G_FILE_TYPE_SYMBOLIC_LINK)))
|
|
continue;
|
|
|
|
child = NULL;
|
|
switch (item.file_type)
|
|
{
|
|
case G_FILE_TYPE_REGULAR:
|
|
log (" Creating file '%s'...\n", item.filename);
|
|
child = create_empty_file (root, item.filename, item.create_flags);
|
|
break;
|
|
case G_FILE_TYPE_DIRECTORY:
|
|
log (" Creating directory '%s'...\n", item.filename);
|
|
child = create_empty_dir (root, item.filename);
|
|
break;
|
|
case G_FILE_TYPE_SYMBOLIC_LINK:
|
|
log (" Creating symlink '%s' --> '%s'...\n", item.filename,
|
|
item.link_to);
|
|
child = create_symlink (root, item.filename, item.link_to);
|
|
break;
|
|
case G_FILE_TYPE_UNKNOWN:
|
|
case G_FILE_TYPE_SPECIAL:
|
|
case G_FILE_TYPE_SHORTCUT:
|
|
case G_FILE_TYPE_MOUNTABLE:
|
|
default:
|
|
break;
|
|
}
|
|
g_assert_nonnull (child);
|
|
|
|
if ((item.mode > 0) && (posix_compat))
|
|
{
|
|
res =
|
|
g_file_set_attribute_uint32 (child, G_FILE_ATTRIBUTE_UNIX_MODE,
|
|
item.mode,
|
|
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
|
NULL, &error);
|
|
g_assert_true (res);
|
|
g_assert_no_error (error);
|
|
}
|
|
|
|
if ((item.extra_flags & TEST_DOT_HIDDEN) == TEST_DOT_HIDDEN)
|
|
{
|
|
gchar *dir, *path, *basename;
|
|
FILE *f;
|
|
|
|
dir = g_path_get_dirname (item.filename);
|
|
basename = g_path_get_basename (item.filename);
|
|
path = g_build_filename (test_data, dir, ".hidden", NULL);
|
|
|
|
f = fopen (path, "a");
|
|
fprintf (f, "%s\n", basename);
|
|
fclose (f);
|
|
|
|
g_free (dir);
|
|
g_free (path);
|
|
g_free (basename);
|
|
}
|
|
|
|
g_object_unref (child);
|
|
}
|
|
|
|
/* create a pattern file */
|
|
log (" Creating pattern file...");
|
|
child = g_file_get_child (root, "pattern_file");
|
|
g_assert_nonnull (child);
|
|
|
|
outs =
|
|
g_file_replace (child, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error);
|
|
g_assert_no_error (error);
|
|
|
|
g_assert_nonnull (outs);
|
|
outds = g_data_output_stream_new (G_OUTPUT_STREAM (outs));
|
|
g_assert_nonnull (outds);
|
|
for (i = 0; i < PATTERN_FILE_SIZE; i++)
|
|
{
|
|
g_data_output_stream_put_byte (outds, i % 256, NULL, &error);
|
|
g_assert_no_error (error);
|
|
}
|
|
|
|
g_output_stream_close (G_OUTPUT_STREAM (outs), NULL, &error);
|
|
g_assert_no_error (error);
|
|
g_object_unref (outds);
|
|
g_object_unref (outs);
|
|
g_object_unref (child);
|
|
log (" done.\n");
|
|
|
|
g_object_unref (root);
|
|
}
|
|
|
|
static GFile *
|
|
file_exists (GFile * parent, const char *filename, gboolean * result)
|
|
{
|
|
GFile *child;
|
|
gboolean res;
|
|
|
|
if (result)
|
|
*result = FALSE;
|
|
|
|
child = g_file_get_child (parent, filename);
|
|
g_assert_nonnull (child);
|
|
res = g_file_query_exists (child, NULL);
|
|
if (result)
|
|
*result = res;
|
|
|
|
return child;
|
|
}
|
|
|
|
static void
|
|
test_attributes (struct StructureItem item, GFileInfo * info)
|
|
{
|
|
GFileType ftype;
|
|
guint32 mode;
|
|
const char *name, *display_name, *edit_name, *copy_name, *symlink_target;
|
|
gboolean utf8_valid;
|
|
gboolean has_attr;
|
|
gboolean is_symlink;
|
|
gboolean is_hidden;
|
|
gboolean can_read, can_write;
|
|
|
|
/* standard::type */
|
|
has_attr = g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_TYPE);
|
|
g_assert_true (has_attr);
|
|
ftype = g_file_info_get_file_type (info);
|
|
g_assert_cmpint (ftype, !=, G_FILE_TYPE_UNKNOWN);
|
|
g_assert_cmpint (ftype, ==, item.file_type);
|
|
|
|
/* unix::mode */
|
|
if ((item.mode > 0) && (posix_compat))
|
|
{
|
|
mode =
|
|
g_file_info_get_attribute_uint32 (info,
|
|
G_FILE_ATTRIBUTE_UNIX_MODE) & 0xFFF;
|
|
g_assert_cmpint (mode, ==, item.mode);
|
|
}
|
|
|
|
/* access::can-read */
|
|
if (item.file_type != G_FILE_TYPE_SYMBOLIC_LINK)
|
|
{
|
|
can_read =
|
|
g_file_info_get_attribute_boolean (info,
|
|
G_FILE_ATTRIBUTE_ACCESS_CAN_READ);
|
|
g_assert_true (can_read);
|
|
}
|
|
|
|
/* access::can-write */
|
|
if ((write_test) && ((item.extra_flags & TEST_OVERWRITE) == TEST_OVERWRITE))
|
|
{
|
|
can_write =
|
|
g_file_info_get_attribute_boolean (info,
|
|
G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE);
|
|
g_assert_true (can_write);
|
|
}
|
|
|
|
/* standard::name */
|
|
name = g_file_info_get_name (info);
|
|
g_assert_nonnull (name);
|
|
|
|
/* standard::display-name */
|
|
display_name = g_file_info_get_display_name (info);
|
|
g_assert_nonnull (display_name);
|
|
utf8_valid = g_utf8_validate (display_name, -1, NULL);
|
|
g_assert_true (utf8_valid);
|
|
|
|
/* standard::edit-name */
|
|
edit_name = g_file_info_get_edit_name (info);
|
|
if (edit_name)
|
|
{
|
|
utf8_valid = g_utf8_validate (edit_name, -1, NULL);
|
|
g_assert_true (utf8_valid);
|
|
}
|
|
|
|
/* standard::copy-name */
|
|
copy_name =
|
|
g_file_info_get_attribute_string (info,
|
|
G_FILE_ATTRIBUTE_STANDARD_COPY_NAME);
|
|
if (copy_name)
|
|
{
|
|
utf8_valid = g_utf8_validate (copy_name, -1, NULL);
|
|
g_assert_true (utf8_valid);
|
|
}
|
|
|
|
/* standard::is-symlink */
|
|
if (posix_compat)
|
|
{
|
|
is_symlink = g_file_info_get_is_symlink (info);
|
|
g_assert_cmpint (is_symlink, ==,
|
|
item.file_type == G_FILE_TYPE_SYMBOLIC_LINK);
|
|
}
|
|
|
|
/* standard::symlink-target */
|
|
if ((item.file_type == G_FILE_TYPE_SYMBOLIC_LINK) && (posix_compat))
|
|
{
|
|
symlink_target = g_file_info_get_symlink_target (info);
|
|
g_assert_cmpstr (symlink_target, ==, item.link_to);
|
|
}
|
|
|
|
/* standard::is-hidden */
|
|
if ((item.extra_flags & TEST_HIDDEN) == TEST_HIDDEN)
|
|
{
|
|
is_hidden =
|
|
g_file_info_get_attribute_boolean (info,
|
|
G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN);
|
|
g_assert_true (is_hidden);
|
|
}
|
|
|
|
/* 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
|
|
test_initial_structure (gconstpointer test_data)
|
|
{
|
|
GFile *root;
|
|
GFile *child;
|
|
gboolean res;
|
|
GError *error;
|
|
GFileInputStream *ins;
|
|
guint i;
|
|
GFileInfo *info;
|
|
guint32 size;
|
|
guchar *buffer;
|
|
gssize read, total_read;
|
|
struct StructureItem item;
|
|
|
|
g_assert_nonnull (test_data);
|
|
log ("\n Testing sample structure in '%s'...\n", (char *) test_data);
|
|
|
|
root = g_file_new_for_commandline_arg ((char *) test_data);
|
|
g_assert_nonnull (root);
|
|
res = g_file_query_exists (root, NULL);
|
|
g_assert_true (res);
|
|
|
|
/* test the structure */
|
|
for (i = 0; i < G_N_ELEMENTS (sample_struct); i++)
|
|
{
|
|
item = sample_struct[i];
|
|
if (((!posix_compat) && (item.file_type == G_FILE_TYPE_SYMBOLIC_LINK))
|
|
|| (item.handle_special))
|
|
continue;
|
|
|
|
log (" Testing file '%s'...\n", item.filename);
|
|
|
|
child = file_exists (root, item.filename, &res);
|
|
g_assert_nonnull (child);
|
|
g_assert_true (res);
|
|
|
|
error = NULL;
|
|
info =
|
|
g_file_query_info (child, "*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
|
NULL, &error);
|
|
g_assert_no_error (error);
|
|
g_assert_nonnull (info);
|
|
|
|
test_attributes (item, info);
|
|
|
|
g_object_unref (child);
|
|
g_object_unref (info);
|
|
}
|
|
|
|
/* read and test the pattern file */
|
|
log (" Testing pattern file...\n");
|
|
child = file_exists (root, "pattern_file", &res);
|
|
g_assert_nonnull (child);
|
|
g_assert_true (res);
|
|
|
|
error = NULL;
|
|
info =
|
|
g_file_query_info (child, "*", G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL,
|
|
&error);
|
|
g_assert_no_error (error);
|
|
g_assert_nonnull (info);
|
|
size = g_file_info_get_size (info);
|
|
g_assert_cmpint (size, ==, PATTERN_FILE_SIZE);
|
|
g_object_unref (info);
|
|
|
|
error = NULL;
|
|
ins = g_file_read (child, NULL, &error);
|
|
g_assert_nonnull (ins);
|
|
g_assert_no_error (error);
|
|
|
|
buffer = g_malloc (PATTERN_FILE_SIZE);
|
|
total_read = 0;
|
|
|
|
while (total_read < PATTERN_FILE_SIZE)
|
|
{
|
|
error = NULL;
|
|
read =
|
|
g_input_stream_read (G_INPUT_STREAM (ins), buffer + total_read,
|
|
PATTERN_FILE_SIZE, NULL, &error);
|
|
g_assert_no_error (error);
|
|
total_read += read;
|
|
log (" read %"G_GSSIZE_FORMAT" bytes, total = %"G_GSSIZE_FORMAT" of %d.\n",
|
|
read, total_read, PATTERN_FILE_SIZE);
|
|
}
|
|
g_assert_cmpint (total_read, ==, PATTERN_FILE_SIZE);
|
|
|
|
error = NULL;
|
|
res = g_input_stream_close (G_INPUT_STREAM (ins), NULL, &error);
|
|
g_assert_no_error (error);
|
|
g_assert_true (res);
|
|
|
|
for (i = 0; i < PATTERN_FILE_SIZE; i++)
|
|
g_assert_cmpint (*(buffer + i), ==, i % 256);
|
|
|
|
g_object_unref (ins);
|
|
g_object_unref (child);
|
|
g_free (buffer);
|
|
g_object_unref (root);
|
|
}
|
|
|
|
static void
|
|
traverse_recurse_dirs (GFile * parent, GFile * root)
|
|
{
|
|
gboolean res;
|
|
GError *error;
|
|
GFileEnumerator *enumerator;
|
|
GFileInfo *info;
|
|
GFile *descend;
|
|
char *relative_path;
|
|
guint i;
|
|
gboolean found;
|
|
|
|
g_assert_nonnull (root);
|
|
|
|
error = NULL;
|
|
enumerator =
|
|
g_file_enumerate_children (parent, "*",
|
|
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL,
|
|
&error);
|
|
g_assert_nonnull (enumerator);
|
|
g_assert_no_error (error);
|
|
|
|
g_assert_true (g_file_enumerator_get_container (enumerator) == parent);
|
|
|
|
error = NULL;
|
|
info = g_file_enumerator_next_file (enumerator, NULL, &error);
|
|
while ((info) && (!error))
|
|
{
|
|
descend = g_file_enumerator_get_child (enumerator, info);
|
|
g_assert_nonnull (descend);
|
|
relative_path = g_file_get_relative_path (root, descend);
|
|
g_assert_nonnull (relative_path);
|
|
|
|
found = FALSE;
|
|
for (i = 0; i < G_N_ELEMENTS (sample_struct); i++)
|
|
{
|
|
if (strcmp (sample_struct[i].filename, relative_path) == 0)
|
|
{
|
|
/* test the attributes again */
|
|
test_attributes (sample_struct[i], info);
|
|
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
g_assert_true (found);
|
|
|
|
log (" Found file %s, relative to root: %s\n",
|
|
g_file_info_get_display_name (info), relative_path);
|
|
|
|
if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
|
|
traverse_recurse_dirs (descend, root);
|
|
|
|
g_object_unref (descend);
|
|
error = NULL;
|
|
g_object_unref (info);
|
|
g_free (relative_path);
|
|
|
|
info = g_file_enumerator_next_file (enumerator, NULL, &error);
|
|
}
|
|
g_assert_no_error (error);
|
|
|
|
error = NULL;
|
|
res = g_file_enumerator_close (enumerator, NULL, &error);
|
|
g_assert_true (res);
|
|
g_assert_no_error (error);
|
|
g_assert_true (g_file_enumerator_is_closed (enumerator));
|
|
|
|
g_object_unref (enumerator);
|
|
}
|
|
|
|
static void
|
|
test_traverse_structure (gconstpointer test_data)
|
|
{
|
|
GFile *root;
|
|
gboolean res;
|
|
|
|
g_assert_nonnull (test_data);
|
|
log ("\n Traversing through the sample structure in '%s'...\n",
|
|
(char *) test_data);
|
|
|
|
root = g_file_new_for_commandline_arg ((char *) test_data);
|
|
g_assert_nonnull (root);
|
|
res = g_file_query_exists (root, NULL);
|
|
g_assert_true (res);
|
|
|
|
traverse_recurse_dirs (root, root);
|
|
|
|
g_object_unref (root);
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
test_enumerate (gconstpointer test_data)
|
|
{
|
|
GFile *root, *child;
|
|
gboolean res;
|
|
GError *error;
|
|
GFileEnumerator *enumerator;
|
|
GFileInfo *info;
|
|
guint i;
|
|
struct StructureItem item;
|
|
|
|
|
|
g_assert_nonnull (test_data);
|
|
log ("\n Test enumerate '%s'...\n", (char *) test_data);
|
|
|
|
root = g_file_new_for_commandline_arg ((char *) test_data);
|
|
g_assert_nonnull (root);
|
|
res = g_file_query_exists (root, NULL);
|
|
g_assert_true (res);
|
|
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (sample_struct); i++)
|
|
{
|
|
item = sample_struct[i];
|
|
if ((!posix_compat) && (item.file_type == G_FILE_TYPE_SYMBOLIC_LINK))
|
|
continue;
|
|
|
|
if (((item.extra_flags & TEST_NOT_EXISTS) == TEST_NOT_EXISTS) ||
|
|
(((item.extra_flags & TEST_NO_ACCESS) == TEST_NO_ACCESS)
|
|
&& posix_compat)
|
|
|| ((item.extra_flags & TEST_ENUMERATE_FILE) ==
|
|
TEST_ENUMERATE_FILE))
|
|
{
|
|
log (" Testing file '%s'\n", item.filename);
|
|
child = g_file_get_child (root, item.filename);
|
|
g_assert_nonnull (child);
|
|
error = NULL;
|
|
enumerator =
|
|
g_file_enumerate_children (child, "*",
|
|
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
|
NULL, &error);
|
|
|
|
if ((item.extra_flags & TEST_NOT_EXISTS) == TEST_NOT_EXISTS)
|
|
{
|
|
g_assert_null (enumerator);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND);
|
|
}
|
|
if ((item.extra_flags & TEST_ENUMERATE_FILE) == TEST_ENUMERATE_FILE)
|
|
{
|
|
g_assert_null (enumerator);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY);
|
|
}
|
|
if ((item.extra_flags & TEST_NO_ACCESS) == TEST_NO_ACCESS)
|
|
{
|
|
g_assert_nonnull (enumerator);
|
|
|
|
error = NULL;
|
|
info = g_file_enumerator_next_file (enumerator, NULL, &error);
|
|
g_assert_null (info);
|
|
g_assert_no_error (error);
|
|
/* no items should be found, no error should be logged */
|
|
}
|
|
|
|
if (error)
|
|
g_error_free (error);
|
|
|
|
if (enumerator)
|
|
{
|
|
error = NULL;
|
|
res = g_file_enumerator_close (enumerator, NULL, &error);
|
|
g_assert_true (res);
|
|
g_assert_no_error (error);
|
|
|
|
g_object_unref (enumerator);
|
|
}
|
|
g_object_unref (child);
|
|
}
|
|
}
|
|
g_object_unref (root);
|
|
}
|
|
|
|
static void
|
|
do_copy_move (GFile * root, struct StructureItem item, const char *target_dir,
|
|
enum StructureExtraFlags extra_flags)
|
|
{
|
|
GFile *dst_dir, *src_file, *dst_file;
|
|
gboolean res;
|
|
GError *error;
|
|
#ifdef G_OS_UNIX
|
|
gboolean have_cap_dac_override = check_cap_dac_override (g_file_peek_path (root));
|
|
#endif
|
|
|
|
log (" do_copy_move: '%s' --> '%s'\n", item.filename, target_dir);
|
|
|
|
dst_dir = g_file_get_child (root, target_dir);
|
|
g_assert_nonnull (dst_dir);
|
|
src_file = g_file_get_child (root, item.filename);
|
|
g_assert_nonnull (src_file);
|
|
dst_file = g_file_get_child (dst_dir, item.filename);
|
|
g_assert_nonnull (dst_file);
|
|
|
|
error = NULL;
|
|
if ((item.extra_flags & TEST_COPY) == TEST_COPY)
|
|
res =
|
|
g_file_copy (src_file, dst_file,
|
|
G_FILE_COPY_NOFOLLOW_SYMLINKS |
|
|
((extra_flags ==
|
|
TEST_OVERWRITE) ? G_FILE_COPY_OVERWRITE :
|
|
G_FILE_COPY_NONE), NULL, NULL, NULL, &error);
|
|
else
|
|
res =
|
|
g_file_move (src_file, dst_file, G_FILE_COPY_NOFOLLOW_SYMLINKS, NULL,
|
|
NULL, NULL, &error);
|
|
|
|
if (error)
|
|
log (" res = %d, error code %d = %s\n", res, error->code,
|
|
error->message);
|
|
|
|
/* copying file/directory to itself (".") */
|
|
if (((item.extra_flags & TEST_NOT_EXISTS) != TEST_NOT_EXISTS) &&
|
|
(extra_flags == TEST_ALREADY_EXISTS))
|
|
{
|
|
g_assert_false (res);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS);
|
|
}
|
|
/* target file is a file, overwrite is not set */
|
|
else if (((item.extra_flags & TEST_NOT_EXISTS) != TEST_NOT_EXISTS) &&
|
|
(extra_flags == TEST_TARGET_IS_FILE))
|
|
{
|
|
g_assert_false (res);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY);
|
|
}
|
|
/* source file is directory */
|
|
else if ((item.extra_flags & TEST_COPY_ERROR_RECURSE) ==
|
|
TEST_COPY_ERROR_RECURSE)
|
|
{
|
|
g_assert_false (res);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE);
|
|
}
|
|
/* source or target path doesn't exist */
|
|
else if (((item.extra_flags & TEST_NOT_EXISTS) == TEST_NOT_EXISTS) ||
|
|
(extra_flags == TEST_NOT_EXISTS))
|
|
{
|
|
g_assert_false (res);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND);
|
|
}
|
|
/* source or target path permission denied */
|
|
else if (((item.extra_flags & TEST_NO_ACCESS) == TEST_NO_ACCESS) ||
|
|
(extra_flags == TEST_NO_ACCESS))
|
|
{
|
|
/* This works for root, see bug #552912 */
|
|
#ifdef G_OS_UNIX
|
|
if (have_cap_dac_override)
|
|
{
|
|
g_test_message ("Unable to exercise g_file_copy() or g_file_move() "
|
|
"failing with EACCES: we probably have "
|
|
"CAP_DAC_OVERRIDE");
|
|
g_assert_true (res);
|
|
g_assert_no_error (error);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
g_assert_false (res);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED);
|
|
}
|
|
}
|
|
/* no error should be found, all exceptions defined above */
|
|
else
|
|
{
|
|
g_assert_true (res);
|
|
g_assert_no_error (error);
|
|
}
|
|
|
|
if (error)
|
|
g_error_free (error);
|
|
|
|
|
|
g_object_unref (dst_dir);
|
|
g_object_unref (src_file);
|
|
g_object_unref (dst_file);
|
|
}
|
|
|
|
static void
|
|
test_copy_move (gconstpointer test_data)
|
|
{
|
|
GFile *root;
|
|
gboolean res;
|
|
guint i;
|
|
struct StructureItem item;
|
|
|
|
log ("\n");
|
|
|
|
g_assert_nonnull (test_data);
|
|
root = g_file_new_for_commandline_arg ((char *) test_data);
|
|
g_assert_nonnull (root);
|
|
res = g_file_query_exists (root, NULL);
|
|
g_assert_true (res);
|
|
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (sample_struct); i++)
|
|
{
|
|
item = sample_struct[i];
|
|
|
|
if ((!posix_compat) && (item.file_type == G_FILE_TYPE_SYMBOLIC_LINK))
|
|
continue;
|
|
|
|
if (((item.extra_flags & TEST_COPY) == TEST_COPY) ||
|
|
((item.extra_flags & TEST_MOVE) == TEST_MOVE))
|
|
{
|
|
/* test copy/move to a directory, expecting no errors if source files exist */
|
|
do_copy_move (root, item, TEST_DIR_TARGET, 0);
|
|
|
|
/* some files have been already moved so we can't count with them in the tests */
|
|
if ((item.extra_flags & TEST_COPY) == TEST_COPY)
|
|
{
|
|
/* test overwrite for flagged files */
|
|
if ((item.extra_flags & TEST_OVERWRITE) == TEST_OVERWRITE)
|
|
{
|
|
do_copy_move (root, item, TEST_DIR_TARGET, TEST_OVERWRITE);
|
|
}
|
|
/* source = target, should return G_IO_ERROR_EXISTS */
|
|
do_copy_move (root, item, ".", TEST_ALREADY_EXISTS);
|
|
/* target is file */
|
|
do_copy_move (root, item, TEST_TARGET_FILE,
|
|
TEST_TARGET_IS_FILE);
|
|
/* target path is invalid */
|
|
do_copy_move (root, item, TEST_NAME_NOT_EXISTS,
|
|
TEST_NOT_EXISTS);
|
|
|
|
/* tests on POSIX-compatible filesystems */
|
|
if (posix_compat)
|
|
{
|
|
/* target directory is not accessible (no execute flag) */
|
|
do_copy_move (root, item, TEST_DIR_NO_ACCESS,
|
|
TEST_NO_ACCESS);
|
|
/* target directory is readonly */
|
|
do_copy_move (root, item, TEST_DIR_NO_WRITE,
|
|
TEST_NO_ACCESS);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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)
|
|
{
|
|
GFile *root, *child;
|
|
gboolean res;
|
|
GError *error;
|
|
guint i;
|
|
struct StructureItem item;
|
|
GFileOutputStream *os;
|
|
|
|
g_assert_nonnull (test_data);
|
|
log ("\n");
|
|
|
|
root = g_file_new_for_commandline_arg ((char *) test_data);
|
|
g_assert_nonnull (root);
|
|
res = g_file_query_exists (root, NULL);
|
|
g_assert_true (res);
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (sample_struct); i++)
|
|
{
|
|
item = sample_struct[i];
|
|
|
|
if (((item.extra_flags & TEST_CREATE) == TEST_CREATE) ||
|
|
((item.extra_flags & TEST_REPLACE) == TEST_REPLACE) ||
|
|
((item.extra_flags & TEST_APPEND) == TEST_APPEND))
|
|
{
|
|
log (" test_create: '%s'\n", item.filename);
|
|
|
|
child = g_file_get_child (root, item.filename);
|
|
g_assert_nonnull (child);
|
|
error = NULL;
|
|
os = NULL;
|
|
|
|
if ((item.extra_flags & TEST_CREATE) == TEST_CREATE)
|
|
os = g_file_create (child, item.create_flags, NULL, &error);
|
|
else if ((item.extra_flags & TEST_REPLACE) == TEST_REPLACE)
|
|
os =
|
|
g_file_replace (child, NULL, TRUE, item.create_flags, NULL,
|
|
&error);
|
|
else if ((item.extra_flags & TEST_APPEND) == TEST_APPEND)
|
|
os = g_file_append_to (child, item.create_flags, NULL, &error);
|
|
|
|
|
|
if (error)
|
|
log (" error code %d = %s\n", error->code, error->message);
|
|
|
|
if (((item.extra_flags & TEST_NOT_EXISTS) == 0) &&
|
|
((item.extra_flags & TEST_CREATE) == TEST_CREATE))
|
|
{
|
|
g_assert_null (os);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS);
|
|
}
|
|
else if (item.file_type == G_FILE_TYPE_DIRECTORY)
|
|
{
|
|
g_assert_null (os);
|
|
if ((item.extra_flags & TEST_CREATE) == TEST_CREATE)
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS);
|
|
else
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY);
|
|
}
|
|
else
|
|
{
|
|
g_assert_nonnull (os);
|
|
g_assert_no_error (error);
|
|
}
|
|
|
|
if (error)
|
|
g_error_free (error);
|
|
|
|
if (os)
|
|
{
|
|
error = NULL;
|
|
res =
|
|
g_output_stream_close (G_OUTPUT_STREAM (os), NULL, &error);
|
|
if (error)
|
|
log (" g_output_stream_close: error %d = %s\n",
|
|
error->code, error->message);
|
|
g_assert_true (res);
|
|
g_assert_no_error (error);
|
|
g_object_unref (os);
|
|
}
|
|
g_object_unref (child);
|
|
}
|
|
}
|
|
g_object_unref (root);
|
|
}
|
|
|
|
static void
|
|
test_open (gconstpointer test_data)
|
|
{
|
|
GFile *root, *child;
|
|
gboolean res;
|
|
GError *error;
|
|
guint i;
|
|
struct StructureItem item;
|
|
GFileInputStream *input_stream;
|
|
|
|
g_assert_nonnull (test_data);
|
|
log ("\n");
|
|
|
|
root = g_file_new_for_commandline_arg ((char *) test_data);
|
|
g_assert_nonnull (root);
|
|
res = g_file_query_exists (root, NULL);
|
|
g_assert_true (res);
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (sample_struct); i++)
|
|
{
|
|
item = sample_struct[i];
|
|
|
|
if ((!posix_compat) && (item.file_type == G_FILE_TYPE_SYMBOLIC_LINK))
|
|
continue;
|
|
|
|
if ((item.extra_flags & TEST_OPEN) == TEST_OPEN)
|
|
{
|
|
log (" test_open: '%s'\n", item.filename);
|
|
|
|
child = g_file_get_child (root, item.filename);
|
|
g_assert_nonnull (child);
|
|
error = NULL;
|
|
input_stream = g_file_read (child, NULL, &error);
|
|
|
|
if (((item.extra_flags & TEST_NOT_EXISTS) == TEST_NOT_EXISTS) ||
|
|
((item.extra_flags & TEST_INVALID_SYMLINK) ==
|
|
TEST_INVALID_SYMLINK))
|
|
{
|
|
g_assert_null (input_stream);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND);
|
|
}
|
|
else if (item.file_type == G_FILE_TYPE_DIRECTORY)
|
|
{
|
|
g_assert_null (input_stream);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY);
|
|
}
|
|
else
|
|
{
|
|
g_assert_nonnull (input_stream);
|
|
g_assert_no_error (error);
|
|
}
|
|
|
|
if (error)
|
|
g_error_free (error);
|
|
|
|
if (input_stream)
|
|
{
|
|
error = NULL;
|
|
res =
|
|
g_input_stream_close (G_INPUT_STREAM (input_stream), NULL,
|
|
&error);
|
|
g_assert_true (res);
|
|
g_assert_no_error (error);
|
|
g_object_unref (input_stream);
|
|
}
|
|
g_object_unref (child);
|
|
}
|
|
}
|
|
g_object_unref (root);
|
|
}
|
|
|
|
static void
|
|
test_delete (gconstpointer test_data)
|
|
{
|
|
GFile *root;
|
|
GFile *child;
|
|
gboolean res;
|
|
GError *error;
|
|
guint i;
|
|
struct StructureItem item;
|
|
gchar *path;
|
|
|
|
g_assert_nonnull (test_data);
|
|
log ("\n");
|
|
|
|
root = g_file_new_for_commandline_arg ((char *) test_data);
|
|
g_assert_nonnull (root);
|
|
res = g_file_query_exists (root, NULL);
|
|
g_assert_true (res);
|
|
|
|
for (i = 0; i < G_N_ELEMENTS (sample_struct); i++)
|
|
{
|
|
item = sample_struct[i];
|
|
|
|
if ((!posix_compat) && (item.file_type == G_FILE_TYPE_SYMBOLIC_LINK))
|
|
continue;
|
|
|
|
if (((item.extra_flags & TEST_DELETE_NORMAL) == TEST_DELETE_NORMAL) ||
|
|
((item.extra_flags & TEST_DELETE_TRASH) == TEST_DELETE_TRASH))
|
|
{
|
|
child = file_exists (root, item.filename, &res);
|
|
g_assert_nonnull (child);
|
|
/* we don't care about result here */
|
|
|
|
path = g_file_get_path (child);
|
|
log (" Deleting %s, path = %s\n", item.filename, path);
|
|
g_free (path);
|
|
|
|
error = NULL;
|
|
if ((item.extra_flags & TEST_DELETE_NORMAL) == TEST_DELETE_NORMAL)
|
|
res = g_file_delete (child, NULL, &error);
|
|
else
|
|
res = g_file_trash (child, NULL, &error);
|
|
|
|
if ((item.extra_flags & TEST_DELETE_NON_EMPTY) ==
|
|
TEST_DELETE_NON_EMPTY)
|
|
{
|
|
g_assert_false (res);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_EMPTY);
|
|
}
|
|
if ((item.extra_flags & TEST_DELETE_FAILURE) == TEST_DELETE_FAILURE)
|
|
{
|
|
g_assert_false (res);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND);
|
|
}
|
|
if ((item.extra_flags & TEST_NOT_EXISTS) == TEST_NOT_EXISTS)
|
|
{
|
|
g_assert_false (res);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND);
|
|
}
|
|
|
|
if (error)
|
|
{
|
|
log (" result = %d, error = %s\n", res, error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
g_object_unref (child);
|
|
}
|
|
}
|
|
g_object_unref (root);
|
|
}
|
|
|
|
static void
|
|
test_make_directory_with_parents (gconstpointer test_data)
|
|
{
|
|
GFile *root, *child, *grandchild, *greatgrandchild;
|
|
gboolean res;
|
|
GError *error = NULL;
|
|
#ifdef G_OS_UNIX
|
|
gboolean have_cap_dac_override = check_cap_dac_override (test_data);
|
|
#endif
|
|
|
|
g_assert_nonnull (test_data);
|
|
|
|
root = g_file_new_for_commandline_arg ((char *) test_data);
|
|
g_assert_nonnull (root);
|
|
res = g_file_query_exists (root, NULL);
|
|
g_assert_true (res);
|
|
|
|
child = g_file_get_child (root, "a");
|
|
grandchild = g_file_get_child (child, "b");
|
|
greatgrandchild = g_file_get_child (grandchild, "c");
|
|
|
|
/* Check that we can successfully make directory hierarchies of
|
|
* depth 1, 2, or 3
|
|
*/
|
|
res = g_file_make_directory_with_parents (child, NULL, &error);
|
|
g_assert_true (res);
|
|
g_assert_no_error (error);
|
|
res = g_file_query_exists (child, NULL);
|
|
g_assert_true (res);
|
|
|
|
g_file_delete (child, NULL, NULL);
|
|
|
|
res = g_file_make_directory_with_parents (grandchild, NULL, &error);
|
|
g_assert_true (res);
|
|
g_assert_no_error (error);
|
|
res = g_file_query_exists (grandchild, NULL);
|
|
g_assert_true (res);
|
|
|
|
g_file_delete (grandchild, NULL, NULL);
|
|
g_file_delete (child, NULL, NULL);
|
|
|
|
res = g_file_make_directory_with_parents (greatgrandchild, NULL, &error);
|
|
g_assert_true (res);
|
|
g_assert_no_error (error);
|
|
res = g_file_query_exists (greatgrandchild, NULL);
|
|
g_assert_true (res);
|
|
|
|
g_file_delete (greatgrandchild, NULL, NULL);
|
|
g_file_delete (grandchild, NULL, NULL);
|
|
g_file_delete (child, NULL, NULL);
|
|
|
|
/* Now test failure by trying to create a directory hierarchy
|
|
* where a ancestor exists but is read-only
|
|
*/
|
|
|
|
/* No obvious way to do this on Windows */
|
|
if (!posix_compat)
|
|
goto out;
|
|
|
|
#ifdef G_OS_UNIX
|
|
/* Permissions are ignored if we have CAP_DAC_OVERRIDE or equivalent,
|
|
* and in particular if we're root */
|
|
if (have_cap_dac_override)
|
|
{
|
|
g_test_skip ("Unable to exercise g_file_make_directory_with_parents "
|
|
"failing with EACCES: we probably have "
|
|
"CAP_DAC_OVERRIDE");
|
|
goto out;
|
|
}
|
|
#endif
|
|
|
|
g_file_make_directory (child, NULL, NULL);
|
|
g_assert_true (res);
|
|
|
|
res = g_file_set_attribute_uint32 (child,
|
|
G_FILE_ATTRIBUTE_UNIX_MODE,
|
|
S_IRUSR + S_IXUSR, /* -r-x------ */
|
|
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
|
NULL, NULL);
|
|
g_assert_true (res);
|
|
|
|
res = g_file_make_directory_with_parents (grandchild, NULL, &error);
|
|
g_assert_false (res);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED);
|
|
g_clear_error (&error);
|
|
|
|
res = g_file_make_directory_with_parents (greatgrandchild, NULL, &error);
|
|
g_assert_false (res);
|
|
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED);
|
|
g_clear_error (&error);
|
|
|
|
out:
|
|
g_object_unref (greatgrandchild);
|
|
g_object_unref (grandchild);
|
|
g_object_unref (child);
|
|
g_object_unref (root);
|
|
}
|
|
|
|
|
|
static void
|
|
cleanup_dir_recurse (GFile *parent, GFile *root)
|
|
{
|
|
gboolean res;
|
|
GError *error;
|
|
GFileEnumerator *enumerator;
|
|
GFileInfo *info;
|
|
GFile *descend;
|
|
char *relative_path;
|
|
|
|
g_assert_nonnull (root);
|
|
|
|
enumerator =
|
|
g_file_enumerate_children (parent, "*",
|
|
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL,
|
|
NULL);
|
|
if (! enumerator)
|
|
return;
|
|
|
|
error = NULL;
|
|
info = g_file_enumerator_next_file (enumerator, NULL, &error);
|
|
while ((info) && (!error))
|
|
{
|
|
descend = g_file_enumerator_get_child (enumerator, info);
|
|
g_assert_nonnull (descend);
|
|
relative_path = g_file_get_relative_path (root, descend);
|
|
g_assert_nonnull (relative_path);
|
|
g_free (relative_path);
|
|
|
|
log (" deleting '%s'\n", g_file_info_get_display_name (info));
|
|
|
|
if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
|
|
cleanup_dir_recurse (descend, root);
|
|
|
|
error = NULL;
|
|
res = g_file_delete (descend, NULL, &error);
|
|
g_assert_true (res);
|
|
|
|
g_object_unref (descend);
|
|
error = NULL;
|
|
g_object_unref (info);
|
|
|
|
info = g_file_enumerator_next_file (enumerator, NULL, &error);
|
|
}
|
|
g_assert_no_error (error);
|
|
|
|
error = NULL;
|
|
res = g_file_enumerator_close (enumerator, NULL, &error);
|
|
g_assert_true (res);
|
|
g_assert_no_error (error);
|
|
|
|
g_object_unref (enumerator);
|
|
}
|
|
|
|
static void
|
|
prep_clean_structure (gconstpointer test_data)
|
|
{
|
|
GFile *root;
|
|
|
|
g_assert_nonnull (test_data);
|
|
log ("\n Cleaning target testing structure in '%s'...\n",
|
|
(char *) test_data);
|
|
|
|
root = g_file_new_for_commandline_arg ((char *) test_data);
|
|
g_assert_nonnull (root);
|
|
|
|
cleanup_dir_recurse (root, root);
|
|
|
|
g_file_delete (root, NULL, NULL);
|
|
|
|
g_object_unref (root);
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
static gboolean only_create_struct;
|
|
const char *target_path;
|
|
GError *error;
|
|
GOptionContext *context;
|
|
|
|
static GOptionEntry cmd_entries[] = {
|
|
{"read-write", 'w', 0, G_OPTION_ARG_NONE, &write_test,
|
|
"Perform write tests (incl. structure creation)", NULL},
|
|
{"create-struct", 'c', 0, G_OPTION_ARG_NONE, &only_create_struct,
|
|
"Only create testing structure (no tests)", NULL},
|
|
{"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Be verbose", NULL},
|
|
{"posix", 'x', 0, G_OPTION_ARG_NONE, &posix_compat,
|
|
"Test POSIX-specific features (unix permissions, symlinks)", NULL},
|
|
{NULL}
|
|
};
|
|
|
|
test_suite = FALSE;
|
|
verbose = FALSE;
|
|
write_test = FALSE;
|
|
only_create_struct = FALSE;
|
|
target_path = NULL;
|
|
posix_compat = FALSE;
|
|
|
|
/* strip all gtester-specific args */
|
|
g_test_init (&argc, &argv, NULL);
|
|
|
|
/* no extra parameters specified, assume we're executed from glib test suite */
|
|
if (argc < 2)
|
|
{
|
|
test_suite = TRUE;
|
|
verbose = TRUE;
|
|
write_test = TRUE;
|
|
only_create_struct = FALSE;
|
|
target_path = DEFAULT_TEST_DIR;
|
|
#ifdef G_PLATFORM_WIN32
|
|
posix_compat = FALSE;
|
|
#else
|
|
posix_compat = TRUE;
|
|
#endif
|
|
}
|
|
|
|
/* add trailing args */
|
|
error = NULL;
|
|
context = g_option_context_new ("target_path");
|
|
g_option_context_add_main_entries (context, cmd_entries, NULL);
|
|
if (!g_option_context_parse (context, &argc, &argv, &error))
|
|
{
|
|
g_printerr ("option parsing failed: %s\n", error->message);
|
|
return g_test_run ();
|
|
}
|
|
|
|
/* remaining arg should is the target path; we don't care of the extra args here */
|
|
if (argc >= 2)
|
|
target_path = strdup (argv[1]);
|
|
|
|
if (! target_path)
|
|
{
|
|
g_printerr ("error: target path was not specified\n");
|
|
g_printerr ("%s", g_option_context_get_help (context, TRUE, NULL));
|
|
return g_test_run ();
|
|
}
|
|
|
|
g_option_context_free (context);
|
|
|
|
/* Write test - clean target directory first */
|
|
/* this can be also considered as a test - enumerate + delete */
|
|
if (write_test || only_create_struct)
|
|
g_test_add_data_func ("/live-g-file/prep_clean_structure", target_path,
|
|
prep_clean_structure);
|
|
|
|
/* Write test - create new testing structure */
|
|
if (write_test || only_create_struct)
|
|
g_test_add_data_func ("/live-g-file/create_structure", target_path,
|
|
test_create_structure);
|
|
|
|
/* Read test - test the sample structure - expect defined attributes to be there */
|
|
if (!only_create_struct)
|
|
g_test_add_data_func ("/live-g-file/test_initial_structure", target_path,
|
|
test_initial_structure);
|
|
|
|
/* Read test - test traverse the structure - no special file should appear */
|
|
if (!only_create_struct)
|
|
g_test_add_data_func ("/live-g-file/test_traverse_structure", target_path,
|
|
test_traverse_structure);
|
|
|
|
/* Read test - enumerate */
|
|
if (!only_create_struct)
|
|
g_test_add_data_func ("/live-g-file/test_enumerate", target_path,
|
|
test_enumerate);
|
|
|
|
/* Read test - open (g_file_read()) */
|
|
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,
|
|
test_create);
|
|
|
|
/* Write test - copy, move */
|
|
if (write_test && (!only_create_struct))
|
|
g_test_add_data_func ("/live-g-file/test_copy_move", target_path,
|
|
test_copy_move);
|
|
|
|
/* Write test - delete, trash */
|
|
if (write_test && (!only_create_struct))
|
|
g_test_add_data_func ("/live-g-file/test_delete", target_path,
|
|
test_delete);
|
|
|
|
/* Write test - make_directory_with_parents */
|
|
if (write_test && (!only_create_struct))
|
|
g_test_add_data_func ("/live-g-file/test_make_directory_with_parents", target_path,
|
|
test_make_directory_with_parents);
|
|
|
|
if (write_test || only_create_struct)
|
|
g_test_add_data_func ("/live-g-file/final_clean", target_path,
|
|
prep_clean_structure);
|
|
|
|
return g_test_run ();
|
|
|
|
}
|