1 /* Linux implementation for renameat2 function.
2 Copyright (C) 2018-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
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.
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.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library. If not, see
17 <https://www.gnu.org/licenses/>. */
19 #include <array_length.h>
25 #include <support/check.h>
26 #include <support/support.h>
27 #include <support/temp_file.h>
28 #include <support/xunistd.h>
31 /* Directory with the temporary files. */
32 static char *directory
;
33 static int directory_fd
;
35 /* Paths within that directory. */
36 static char *old_path
; /* File is called "old". */
37 static char *new_path
; /* File is called "new". */
39 /* Subdirectory within the directory above. */
40 static char *subdirectory
;
43 /* And a pathname in that directory (called "file"). */
44 static char *subdir_path
;
47 prepare (int argc
, char **argv
)
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
);
63 /* Delete all files, preparing a clean slate for the next test. */
65 delete_all_files (void)
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
]);
73 /* Return true if PATH exists in the file system. */
75 file_exists (const char *path
)
77 return access (path
, F_OK
) == 0;
80 /* Check that PATH exists and has size EXPECTED_SIZE. */
82 check_size (const char *path
, off64_t expected_size
)
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
);
92 /* Rename tests where the target does not exist. */
94 rename_without_existing_target (unsigned int flags
)
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
));
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
));
109 support_write_file_string (old_path
, "");
110 TEST_COMPARE (renameat2 (directory_fd
, "old", subdirectory_fd
, "file", 0),
112 TEST_VERIFY (!file_exists (old_path
));
113 TEST_VERIFY (file_exists (subdir_path
));
119 /* Tests with zero flags argument. These are expected to succeed
120 because this renameat2 variant can be implemented with
122 rename_without_existing_target (0);
124 /* renameat2 without flags replaces an existing destination. */
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);
132 /* Now we need to check for kernel support of renameat2 with
135 support_write_file_string (old_path
, "");
136 if (renameat2 (AT_FDCWD
, old_path
, AT_FDCWD
, new_path
, RENAME_NOREPLACE
)
140 puts ("warning: no support for renameat2 with flags");
142 FAIL_EXIT1 ("renameat2 probe failed: %m");
146 /* We have full renameat2 support. */
147 rename_without_existing_target (RENAME_NOREPLACE
);
149 /* Now test RENAME_NOREPLACE with an existing target. */
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);
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);
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);
177 /* The flag combination of RENAME_NOREPLACE and RENAME_EXCHANGE
179 TEST_COMPARE (renameat2 (directory_fd
, "ignored",
180 subdirectory_fd
, "ignored",
181 RENAME_NOREPLACE
| RENAME_EXCHANGE
), -1);
182 TEST_COMPARE (errno
, EINVAL
);
185 /* Create all the pathnames to avoid warnings from the test
187 support_write_file_string (old_path
, "");
188 support_write_file_string (new_path
, "");
189 support_write_file_string (subdir_path
, "");
197 xclose (directory_fd
);
198 xclose (subdirectory_fd
);
203 #define PREPARE prepare
204 #include <support/test-driver.c>