Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2018 Red Hat, Inc.
3 : : *
4 : : * SPDX-License-Identifier: LGPL-2.1-or-later
5 : : *
6 : : * This library is free software; you can redistribute it and/or modify
7 : : * it under the terms of the GNU Lesser General Public License as
8 : : * published by the Free Software Foundation; either version 2.1 of the
9 : : * licence, or (at your option) any later version.
10 : : *
11 : : * This is distributed in the hope that it will be useful, but WITHOUT
12 : : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 : : * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14 : : * License for more details.
15 : : *
16 : : * You should have received a copy of the GNU Lesser General Public License
17 : : * along with this library; if not, see <http://www.gnu.org/licenses/>.
18 : : */
19 : :
20 : : #include "config.h"
21 : :
22 : : #include <glib.h>
23 : :
24 : : #ifndef G_OS_UNIX
25 : : #error This is a Unix-specific test
26 : : #endif
27 : :
28 : : #include <glib/gstdio.h>
29 : : #include <gio/gio.h>
30 : : #include <gio/gunixmounts.h>
31 : : #include <fcntl.h>
32 : :
33 : : #ifdef HAVE_COCOA
34 : : static gchar *
35 : : create_home_tmp_file (const gchar *tmpl)
36 : : {
37 : : GFile *file = NULL;
38 : : GFileIOStream *stream = NULL;
39 : : GError *error = NULL;
40 : : gchar *path;
41 : :
42 : : file = g_file_new_tmp (tmpl, &stream, &error);
43 : : g_assert_no_error (error);
44 : : path = g_strdup (g_file_peek_path (file));
45 : :
46 : : g_assert_true (g_io_stream_close (G_IO_STREAM (stream), NULL, &error));
47 : : g_assert_no_error (error);
48 : :
49 : : g_object_unref (stream);
50 : : g_object_unref (file);
51 : :
52 : : return path;
53 : : }
54 : :
55 : : static void
56 : : remove_trashed_home_file (const gchar *basename)
57 : : {
58 : : g_autofree gchar *trashed_path = NULL;
59 : :
60 : : trashed_path = g_build_filename (g_get_home_dir (), ".Trash", basename, NULL);
61 : : g_remove (trashed_path);
62 : : }
63 : :
64 : : static void
65 : : test_trash_macos_native (void)
66 : : {
67 : : g_autofree gchar *filepath = NULL;
68 : : g_autofree gchar *basename = NULL;
69 : : g_autofree gchar *legacy_trash_path = NULL;
70 : : g_autoptr (GFile) file = NULL;
71 : : GError *error = NULL;
72 : :
73 : : filepath = create_home_tmp_file ("test-trash-macos-XXXXXX");
74 : : basename = g_path_get_basename (filepath);
75 : : legacy_trash_path = g_build_filename (g_get_user_data_dir (), "Trash", "files", basename, NULL);
76 : : file = g_file_new_for_path (filepath);
77 : :
78 : : g_assert_true (g_file_trash (file, NULL, &error));
79 : : g_assert_no_error (error);
80 : : g_assert_false (g_file_test (filepath, G_FILE_TEST_EXISTS));
81 : : g_assert_false (g_file_test (legacy_trash_path, G_FILE_TEST_EXISTS));
82 : :
83 : : remove_trashed_home_file (basename);
84 : : }
85 : : #endif
86 : :
87 : : /* Test that g_file_trash() returns G_IO_ERROR_NOT_SUPPORTED for files on system mounts. */
88 : : static void
89 : 1 : test_trash_not_supported (void)
90 : : {
91 : : #ifdef HAVE_COCOA
92 : : g_test_skip ("This test covers the freedesktop trash implementation, not the macOS native backend");
93 : : return;
94 : : #else
95 : : GFile *file;
96 : : GFileIOStream *stream;
97 : : GUnixMountEntry *mount;
98 : : GFileInfo *info;
99 : 1 : GError *error = NULL;
100 : : gboolean ret;
101 : : gchar *parent_dirname;
102 : : GStatBuf parent_stat, home_stat;
103 : :
104 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/251");
105 : :
106 : : /* The test assumes that tmp file is located on system internal mount. */
107 : 1 : file = g_file_new_tmp ("test-trashXXXXXX", &stream, &error);
108 : 1 : parent_dirname = g_path_get_dirname (g_file_peek_path (file));
109 : 1 : g_assert_no_error (error);
110 : 1 : g_assert_cmpint (g_stat (parent_dirname, &parent_stat), ==, 0);
111 : 1 : g_test_message ("File: %s (parent st_dev: %" G_GUINT64_FORMAT ")",
112 : 1 : g_file_peek_path (file), (guint64) parent_stat.st_dev);
113 : :
114 : 1 : g_assert_cmpint (g_stat (g_get_home_dir (), &home_stat), ==, 0);
115 : 1 : g_test_message ("Home: %s (st_dev: %" G_GUINT64_FORMAT ")",
116 : 1 : g_get_home_dir (), (guint64) home_stat.st_dev);
117 : :
118 : 1 : if (parent_stat.st_dev == home_stat.st_dev)
119 : : {
120 : 0 : g_test_skip ("The file has to be on another filesystem than the home trash to run this test");
121 : :
122 : 0 : g_free (parent_dirname);
123 : 0 : g_object_unref (stream);
124 : 0 : g_object_unref (file);
125 : :
126 : 0 : return;
127 : : }
128 : :
129 : 1 : mount = g_unix_mount_entry_for (g_file_peek_path (file), NULL);
130 : 1 : g_assert_true (mount == NULL || g_unix_mount_entry_is_system_internal (mount));
131 : 1 : g_test_message ("Mount: %s", (mount != NULL) ? g_unix_mount_entry_get_mount_path (mount) : "(null)");
132 : 1 : g_clear_pointer (&mount, g_unix_mount_entry_free);
133 : :
134 : : /* g_file_trash() shouldn't be supported on system internal mounts,
135 : : * because those are not monitored by gvfsd-trash.
136 : : */
137 : 1 : ret = g_file_trash (file, NULL, &error);
138 : 1 : g_assert_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED);
139 : 1 : g_test_message ("Error: %s", error->message);
140 : 1 : g_assert_false (ret);
141 : 1 : g_clear_error (&error);
142 : :
143 : 1 : info = g_file_query_info (file,
144 : : G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH,
145 : : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
146 : : NULL,
147 : : &error);
148 : 1 : g_assert_no_error (error);
149 : :
150 : 1 : g_assert_false (g_file_info_get_attribute_boolean (info,
151 : : G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH));
152 : :
153 : 1 : g_io_stream_close (G_IO_STREAM (stream), NULL, &error);
154 : 1 : g_assert_no_error (error);
155 : :
156 : 1 : g_free (parent_dirname);
157 : 1 : g_object_unref (info);
158 : 1 : g_object_unref (stream);
159 : 1 : g_object_unref (file);
160 : : #endif
161 : : }
162 : :
163 : : /* Test that symlinks are properly expanded when looking for topdir (e.g. for trash folder). */
164 : : static void
165 : 1 : test_trash_symlinks (void)
166 : : {
167 : : #ifdef HAVE_COCOA
168 : : g_test_skip ("This test covers topdir lookup for the freedesktop trash implementation, which is not supported on macOS");
169 : : return;
170 : : #else
171 : : GFile *symlink;
172 : : GUnixMountEntry *target_mount, *tmp_mount, *symlink_mount, *target_over_symlink_mount;
173 : : gchar *target, *tmp, *target_over_symlink;
174 : 1 : GError *error = NULL;
175 : :
176 : 1 : g_test_bug ("https://gitlab.gnome.org/GNOME/glib/issues/1522");
177 : :
178 : 1 : target = g_build_filename (g_get_home_dir (), ".local", NULL);
179 : :
180 : 1 : if (!g_file_test (target, G_FILE_TEST_IS_DIR))
181 : : {
182 : 1 : g_test_skip_printf ("Directory '%s' does not exist", target);
183 : 1 : g_free (target);
184 : 1 : return;
185 : : }
186 : :
187 : 0 : target_mount = g_unix_mount_entry_for (target, NULL);
188 : :
189 : 0 : if (target_mount == NULL)
190 : : {
191 : 0 : g_test_skip_printf ("Unable to determine mount point for %s", target);
192 : 0 : g_free (target);
193 : 0 : return;
194 : : }
195 : :
196 : 0 : g_assert_nonnull (target_mount);
197 : 0 : g_test_message ("Target: %s (mount: %s)", target, g_unix_mount_entry_get_mount_path (target_mount));
198 : :
199 : 0 : tmp = g_dir_make_tmp ("test-trashXXXXXX", &error);
200 : 0 : g_assert_no_error (error);
201 : 0 : g_assert_nonnull (tmp);
202 : 0 : tmp_mount = g_unix_mount_entry_for (tmp, NULL);
203 : :
204 : 0 : if (tmp_mount == NULL)
205 : : {
206 : 0 : g_test_skip_printf ("Unable to determine mount point for %s", tmp);
207 : 0 : g_unix_mount_entry_free (target_mount);
208 : 0 : g_free (target);
209 : 0 : g_free (tmp);
210 : 0 : return;
211 : : }
212 : :
213 : 0 : g_assert_nonnull (tmp_mount);
214 : 0 : g_test_message ("Tmp: %s (mount: %s)", tmp, g_unix_mount_entry_get_mount_path (tmp_mount));
215 : :
216 : 0 : if (g_unix_mount_entry_compare (target_mount, tmp_mount) == 0)
217 : : {
218 : 0 : g_test_skip ("The tmp has to be on another mount than the home to run this test");
219 : :
220 : 0 : g_unix_mount_entry_free (tmp_mount);
221 : 0 : g_free (tmp);
222 : 0 : g_unix_mount_entry_free (target_mount);
223 : 0 : g_free (target);
224 : :
225 : 0 : return;
226 : : }
227 : :
228 : 0 : symlink = g_file_new_build_filename (tmp, "symlink", NULL);
229 : 0 : g_file_make_symbolic_link (symlink, g_get_home_dir (), NULL, &error);
230 : 0 : g_assert_no_error (error);
231 : :
232 : 0 : symlink_mount = g_unix_mount_entry_for (g_file_peek_path (symlink), NULL);
233 : 0 : g_assert_nonnull (symlink_mount);
234 : 0 : g_test_message ("Symlink: %s (mount: %s)", g_file_peek_path (symlink), g_unix_mount_entry_get_mount_path (symlink_mount));
235 : :
236 : 0 : g_assert_cmpint (g_unix_mount_entry_compare (symlink_mount, tmp_mount), ==, 0);
237 : :
238 : 0 : target_over_symlink = g_build_filename (g_file_peek_path (symlink),
239 : : ".local",
240 : : NULL);
241 : 0 : target_over_symlink_mount = g_unix_mount_entry_for (target_over_symlink, NULL);
242 : 0 : g_assert_nonnull (symlink_mount);
243 : 0 : g_test_message ("Target over symlink: %s (mount: %s)", target_over_symlink, g_unix_mount_entry_get_mount_path (target_over_symlink_mount));
244 : :
245 : 0 : g_assert_cmpint (g_unix_mount_entry_compare (target_over_symlink_mount, target_mount), ==, 0);
246 : :
247 : 0 : g_unix_mount_entry_free (target_over_symlink_mount);
248 : 0 : g_unix_mount_entry_free (symlink_mount);
249 : 0 : g_free (target_over_symlink);
250 : 0 : g_object_unref (symlink);
251 : 0 : g_unix_mount_entry_free (tmp_mount);
252 : 0 : g_free (tmp);
253 : 0 : g_unix_mount_entry_free (target_mount);
254 : 0 : g_free (target);
255 : : #endif
256 : : }
257 : :
258 : : /* Test that long filename are handled correctly */
259 : : static void
260 : 1 : test_trash_long_filename (void)
261 : : {
262 : 1 : const gchar *long_filename = "test_trash_long_filename_aaaaaaaaaaaaaaaaaaaaaaaaa" \
263 : : "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
264 : : "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
265 : : "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
266 : : "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
267 : : "aaaaa"; /* 255 bytes */
268 : : gchar *filepath;
269 : : int fd;
270 : : GFile *file;
271 : 1 : GError *error = NULL;
272 : :
273 : : /* The test assumes that test file is located on ext fs. */
274 : 1 : filepath = g_build_filename (g_get_home_dir (), long_filename, NULL);
275 : 1 : fd = g_open (filepath, O_CREAT | O_RDONLY, 0666);
276 : 1 : if (fd == -1)
277 : : {
278 : 0 : g_test_skip ("Failed to create test file");
279 : 0 : g_free (filepath);
280 : 0 : return;
281 : : }
282 : 1 : (void) g_close (fd, NULL);
283 : 1 : file = g_file_new_for_path (filepath);
284 : 1 : g_file_trash (file, NULL, &error);
285 : 1 : g_unlink (filepath);
286 : 1 : g_assert_no_error (error);
287 : :
288 : : /* Delete trashed version of test file */
289 : : #ifdef HAVE_COCOA
290 : : {
291 : : g_autofree gchar *basename = g_path_get_basename (filepath);
292 : : remove_trashed_home_file (basename);
293 : : }
294 : : #else
295 : : {
296 : : GFileEnumerator *enumerator;
297 : : GFile *trash;
298 : :
299 : 1 : trash = g_file_new_for_uri ("trash:///");
300 : 1 : enumerator = g_file_enumerate_children (trash,
301 : : G_FILE_ATTRIBUTE_STANDARD_NAME ","
302 : : G_FILE_ATTRIBUTE_TRASH_ORIG_PATH,
303 : : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
304 : : NULL, NULL);
305 : :
306 : 1 : if (enumerator)
307 : : {
308 : : GFileInfo *info;
309 : :
310 : 0 : while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)) != NULL)
311 : : {
312 : 0 : const char *origpath = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_TRASH_ORIG_PATH);
313 : :
314 : 0 : if (strcmp (filepath, origpath) == 0)
315 : : {
316 : 0 : GFile *item = g_file_get_child (trash, g_file_info_get_name (info));
317 : 0 : g_file_delete (item, NULL, NULL);
318 : 0 : g_object_unref (item);
319 : 0 : g_object_unref (info);
320 : 0 : break;
321 : : }
322 : :
323 : 0 : g_object_unref (info);
324 : : }
325 : :
326 : 0 : g_file_enumerator_close (enumerator, NULL, NULL);
327 : 0 : g_object_unref (enumerator);
328 : : }
329 : 1 : g_object_unref (trash);
330 : : }
331 : : #endif
332 : :
333 : 1 : g_free (filepath);
334 : 1 : g_object_unref (file);
335 : 1 : g_clear_error (&error);
336 : : }
337 : :
338 : : int
339 : 1 : main (int argc, char *argv[])
340 : : {
341 : 1 : g_test_init (&argc, &argv, NULL);
342 : :
343 : : #ifdef HAVE_COCOA
344 : : g_test_add_func ("/trash/macos/native", test_trash_macos_native);
345 : : #endif
346 : 1 : g_test_add_func ("/trash/not-supported", test_trash_not_supported);
347 : 1 : g_test_add_func ("/trash/symlinks", test_trash_symlinks);
348 : 1 : g_test_add_func ("/trash/long-filename", test_trash_long_filename);
349 : :
350 : 1 : return g_test_run ();
351 : : }
|