Add renameat2 function [BZ #17662]
[platform/upstream/glibc.git] / stdio-common / tst-renameat2.c
1 /* Linux implementation for renameat2 function.
2    Copyright (C) 2018 Free Software Foundation, Inc.
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
17    <http://www.gnu.org/licenses/>.  */
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>