]>
Commit | Line | Data |
---|---|---|
d6da5cb6 | 1 | /* Linux implementation for renameat2 function. |
04277e02 | 2 | Copyright (C) 2018-2019 Free Software Foundation, Inc. |
d6da5cb6 FW |
3 | This file is part of the GNU C Library. |
4 | ||
5 | The GNU C Library is free software; you can redistribute it and/or | |
6 | modify it under the terms of the GNU Lesser General Public | |
7 | License as published by the Free Software Foundation; either | |
8 | version 2.1 of the License, or (at your option) any later version. | |
9 | ||
10 | The GNU C Library is distributed in the hope that it will be useful, | |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | Lesser General Public License for more details. | |
14 | ||
15 | You should have received a copy of the GNU Lesser General Public | |
16 | License along with the GNU C Library. If not, see | |
5a82c748 | 17 | <https://www.gnu.org/licenses/>. */ |
d6da5cb6 FW |
18 | |
19 | #include <array_length.h> | |
20 | #include <errno.h> | |
21 | #include <fcntl.h> | |
22 | #include <stdbool.h> | |
23 | #include <stdio.h> | |
24 | #include <stdlib.h> | |
25 | #include <support/check.h> | |
26 | #include <support/support.h> | |
27 | #include <support/temp_file.h> | |
28 | #include <support/xunistd.h> | |
29 | #include <unistd.h> | |
30 | ||
31 | /* Directory with the temporary files. */ | |
32 | static char *directory; | |
33 | static int directory_fd; | |
34 | ||
35 | /* Paths within that directory. */ | |
36 | static char *old_path; /* File is called "old". */ | |
37 | static char *new_path; /* File is called "new". */ | |
38 | ||
39 | /* Subdirectory within the directory above. */ | |
40 | static char *subdirectory; | |
41 | int subdirectory_fd; | |
42 | ||
43 | /* And a pathname in that directory (called "file"). */ | |
44 | static char *subdir_path; | |
45 | ||
46 | static void | |
47 | prepare (int argc, char **argv) | |
48 | { | |
49 | directory = support_create_temp_directory ("tst-renameat2-"); | |
50 | directory_fd = xopen (directory, O_RDONLY | O_DIRECTORY, 0); | |
51 | old_path = xasprintf ("%s/old", directory); | |
52 | add_temp_file (old_path); | |
53 | new_path = xasprintf ("%s/new", directory); | |
54 | add_temp_file (new_path); | |
55 | subdirectory = xasprintf ("%s/subdir", directory); | |
56 | xmkdir (subdirectory, 0777); | |
57 | add_temp_file (subdirectory); | |
58 | subdirectory_fd = xopen (subdirectory, O_RDONLY | O_DIRECTORY, 0); | |
59 | subdir_path = xasprintf ("%s/file", subdirectory); | |
60 | add_temp_file (subdir_path); | |
61 | } | |
62 | ||
63 | /* Delete all files, preparing a clean slate for the next test. */ | |
64 | static void | |
65 | delete_all_files (void) | |
66 | { | |
67 | char *files[] = { old_path, new_path, subdir_path }; | |
68 | for (size_t i = 0; i < array_length (files); ++i) | |
69 | if (unlink (files[i]) != 0 && errno != ENOENT) | |
70 | FAIL_EXIT1 ("unlink (\"%s\"): %m", files[i]); | |
71 | } | |
72 | ||
73 | /* Return true if PATH exists in the file system. */ | |
74 | static bool | |
75 | file_exists (const char *path) | |
76 | { | |
77 | return access (path, F_OK) == 0; | |
78 | } | |
79 | ||
80 | /* Check that PATH exists and has size EXPECTED_SIZE. */ | |
81 | static void | |
82 | check_size (const char *path, off64_t expected_size) | |
83 | { | |
84 | struct stat64 st; | |
85 | xstat (path, &st); | |
86 | if (st.st_size != expected_size) | |
87 | FAIL_EXIT1 ("file \"%s\": expected size %lld, actual size %lld", | |
88 | path, (unsigned long long int) expected_size, | |
89 | (unsigned long long int) st.st_size); | |
90 | } | |
91 | ||
92 | /* Rename tests where the target does not exist. */ | |
93 | static void | |
94 | rename_without_existing_target (unsigned int flags) | |
95 | { | |
96 | delete_all_files (); | |
97 | support_write_file_string (old_path, ""); | |
98 | TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, flags), 0); | |
99 | TEST_VERIFY (!file_exists (old_path)); | |
100 | TEST_VERIFY (file_exists (new_path)); | |
101 | ||
102 | delete_all_files (); | |
103 | support_write_file_string (old_path, ""); | |
104 | TEST_COMPARE (renameat2 (directory_fd, "old", AT_FDCWD, new_path, flags), 0); | |
105 | TEST_VERIFY (!file_exists (old_path)); | |
106 | TEST_VERIFY (file_exists (new_path)); | |
107 | ||
108 | delete_all_files (); | |
109 | support_write_file_string (old_path, ""); | |
110 | TEST_COMPARE (renameat2 (directory_fd, "old", subdirectory_fd, "file", 0), | |
111 | 0); | |
112 | TEST_VERIFY (!file_exists (old_path)); | |
113 | TEST_VERIFY (file_exists (subdir_path)); | |
114 | } | |
115 | ||
116 | static int | |
117 | do_test (void) | |
118 | { | |
119 | /* Tests with zero flags argument. These are expected to succeed | |
120 | because this renameat2 variant can be implemented with | |
121 | renameat. */ | |
122 | rename_without_existing_target (0); | |
123 | ||
124 | /* renameat2 without flags replaces an existing destination. */ | |
125 | delete_all_files (); | |
126 | support_write_file_string (old_path, "123"); | |
127 | support_write_file_string (new_path, "1234"); | |
128 | TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, 0), 0); | |
129 | TEST_VERIFY (!file_exists (old_path)); | |
130 | check_size (new_path, 3); | |
131 | ||
132 | /* Now we need to check for kernel support of renameat2 with | |
133 | flags. */ | |
134 | delete_all_files (); | |
135 | support_write_file_string (old_path, ""); | |
136 | if (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, RENAME_NOREPLACE) | |
137 | != 0) | |
138 | { | |
139 | if (errno == EINVAL) | |
140 | puts ("warning: no support for renameat2 with flags"); | |
141 | else | |
142 | FAIL_EXIT1 ("renameat2 probe failed: %m"); | |
143 | } | |
144 | else | |
145 | { | |
146 | /* We have full renameat2 support. */ | |
147 | rename_without_existing_target (RENAME_NOREPLACE); | |
148 | ||
149 | /* Now test RENAME_NOREPLACE with an existing target. */ | |
150 | delete_all_files (); | |
151 | support_write_file_string (old_path, "123"); | |
152 | support_write_file_string (new_path, "1234"); | |
153 | TEST_COMPARE (renameat2 (AT_FDCWD, old_path, AT_FDCWD, new_path, | |
154 | RENAME_NOREPLACE), -1); | |
155 | TEST_COMPARE (errno, EEXIST); | |
156 | check_size (old_path, 3); | |
157 | check_size (new_path, 4); | |
158 | ||
159 | delete_all_files (); | |
160 | support_write_file_string (old_path, "123"); | |
161 | support_write_file_string (new_path, "1234"); | |
162 | TEST_COMPARE (renameat2 (directory_fd, "old", AT_FDCWD, new_path, | |
163 | RENAME_NOREPLACE), -1); | |
164 | TEST_COMPARE (errno, EEXIST); | |
165 | check_size (old_path, 3); | |
166 | check_size (new_path, 4); | |
167 | ||
168 | delete_all_files (); | |
169 | support_write_file_string (old_path, "123"); | |
170 | support_write_file_string (subdir_path, "1234"); | |
171 | TEST_COMPARE (renameat2 (directory_fd, "old", subdirectory_fd, "file", | |
172 | RENAME_NOREPLACE), -1); | |
173 | TEST_COMPARE (errno, EEXIST); | |
174 | check_size (old_path, 3); | |
175 | check_size (subdir_path, 4); | |
176 | ||
177 | /* The flag combination of RENAME_NOREPLACE and RENAME_EXCHANGE | |
178 | is invalid. */ | |
179 | TEST_COMPARE (renameat2 (directory_fd, "ignored", | |
180 | subdirectory_fd, "ignored", | |
181 | RENAME_NOREPLACE | RENAME_EXCHANGE), -1); | |
182 | TEST_COMPARE (errno, EINVAL); | |
183 | } | |
184 | ||
185 | /* Create all the pathnames to avoid warnings from the test | |
186 | harness. */ | |
187 | support_write_file_string (old_path, ""); | |
188 | support_write_file_string (new_path, ""); | |
189 | support_write_file_string (subdir_path, ""); | |
190 | ||
191 | free (directory); | |
192 | free (subdirectory); | |
193 | free (old_path); | |
194 | free (new_path); | |
195 | free (subdir_path); | |
196 | ||
197 | xclose (directory_fd); | |
198 | xclose (subdirectory_fd); | |
199 | ||
200 | return 0; | |
201 | } | |
202 | ||
203 | #define PREPARE prepare | |
204 | #include <support/test-driver.c> |