support: Also return fd when it is 0
[glibc.git] / io / tst-open-tmpfile.c
blobb3fd15c64a0dacb2ed52f6994aedea2a56239fa0
1 /* Test open and openat with O_TMPFILE.
2 Copyright (C) 2016-2021 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 /* This test verifies that open and openat work as expected, i.e. they
20 create a deleted file with the requested file mode. */
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdbool.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <unistd.h>
31 #include <support/support.h>
33 #ifdef O_TMPFILE
34 typedef int (*wrapper_func) (const char *, int, mode_t);
36 /* Error-checking wrapper for the open function, compatible with the
37 wrapper_func type. */
38 static int
39 wrap_open (const char *path, int flags, mode_t mode)
41 int ret = open (path, flags, mode);
42 if (ret < 0)
44 printf ("error: open (\"%s\", 0x%x, 0%03o): %m\n", path, flags, mode);
45 exit (1);
47 return ret;
50 /* Error-checking wrapper for the openat function, compatible with the
51 wrapper_func type. */
52 static int
53 wrap_openat (const char *path, int flags, mode_t mode)
55 int ret = openat (AT_FDCWD, path, flags, mode);
56 if (ret < 0)
58 printf ("error: openat (\"%s\", 0x%x, 0%03o): %m\n", path, flags, mode);
59 exit (1);
61 return ret;
64 /* Error-checking wrapper for the open64 function, compatible with the
65 wrapper_func type. */
66 static int
67 wrap_open64 (const char *path, int flags, mode_t mode)
69 int ret = open64 (path, flags, mode);
70 if (ret < 0)
72 printf ("error: open64 (\"%s\", 0x%x, 0%03o): %m\n", path, flags, mode);
73 exit (1);
75 return ret;
78 /* Error-checking wrapper for the openat64 function, compatible with the
79 wrapper_func type. */
80 static int
81 wrap_openat64 (const char *path, int flags, mode_t mode)
83 int ret = openat64 (AT_FDCWD, path, flags, mode);
84 if (ret < 0)
86 printf ("error: openat64 (\"%s\", 0x%x, 0%03o): %m\n", path, flags, mode);
87 exit (1);
89 return ret;
92 /* Return true if FD is flagged as deleted in /proc/self/fd, false if
93 not. */
94 static bool
95 is_file_deteted (int fd)
97 char *proc_fd_path = xasprintf ("/proc/self/fd/%d", fd);
98 char file_path[4096];
99 ssize_t file_path_length
100 = readlink (proc_fd_path, file_path, sizeof (file_path));
101 if (file_path_length < 0)
103 printf ("error: readlink (\"%s\"): %m", proc_fd_path);
104 free (proc_fd_path);
105 exit (1);
107 free (proc_fd_path);
108 if (file_path_length == sizeof (file_path))
110 printf ("error: path in /proc resolves to overlong file name: %.*s\n",
111 (int) file_path_length, file_path);
112 exit (1);
114 const char *deleted = " (deleted)";
115 if (file_path_length < strlen (deleted))
117 printf ("error: path in /proc is too short: %.*s\n",
118 (int) file_path_length, file_path);
119 exit (1);
121 return memcmp (file_path + file_path_length - strlen (deleted),
122 deleted, strlen (deleted)) == 0;
125 /* Obtain a file name which is difficult to guess. */
126 static char *
127 get_random_name (void)
129 unsigned long long bytes[2];
130 int random_device = open ("/dev/urandom", O_RDONLY);
131 if (random_device < 0)
133 printf ("error: open (\"/dev/urandom\"): %m\n");
134 exit (1);
136 ssize_t ret = read (random_device, bytes, sizeof (bytes));
137 if (ret < 0)
139 printf ("error: read (\"/dev/urandom\"): %m\n");
140 exit (1);
142 if (ret != sizeof (bytes))
144 printf ("error: short read from /dev/urandom: %zd\n", ret);
145 exit (1);
147 close (random_device);
148 return xasprintf ("tst-open-tmpfile-%08llx%08llx.tmp", bytes[0], bytes[1]);
151 /* Check open/openat (as specified by OP and WRAPPER) with a specific
152 PATH/FLAGS/MODE combination. */
153 static void
154 check_wrapper_flags_mode (const char *op, wrapper_func wrapper,
155 const char *path, int flags, mode_t mode)
157 int fd = wrapper (path, flags | O_TMPFILE, mode);
158 struct stat64 st;
159 if (fstat64 (fd, &st) != 0)
161 printf ("error: fstat64: %m\n");
162 exit (1);
165 /* Verify that the mode was correctly processed. */
166 int actual_mode = st.st_mode & 0777;
167 if (actual_mode != mode)
169 printf ("error: unexpected mode; expected 0%03o, actual 0%03o\n",
170 mode, actual_mode);
171 exit (1);
174 /* Check that the file is marked as deleted in /proc. */
175 if (!is_file_deteted (fd))
177 printf ("error: path in /proc is not marked as deleted\n");
178 exit (1);
181 /* Check that the file can be turned into a regular file with
182 linkat. Open a file descriptor for the directory at PATH. Use
183 AT_FDCWD if PATH is ".", to exercise that functionality as
184 well. */
185 int path_fd;
186 if (strcmp (path, ".") == 0)
187 path_fd = AT_FDCWD;
188 else
190 path_fd = open (path, O_RDONLY | O_DIRECTORY);
191 if (path_fd < 0)
193 printf ("error: open (\"%s\"): %m\n", path);
194 exit (1);
198 /* Use a hard-to-guess name for the new directory entry. */
199 char *new_name = get_random_name ();
201 /* linkat does not require privileges if the path in /proc/self/fd
202 is used. */
203 char *proc_fd_path = xasprintf ("/proc/self/fd/%d", fd);
204 if (linkat (AT_FDCWD, proc_fd_path, path_fd, new_name,
205 AT_SYMLINK_FOLLOW) == 0)
207 if (unlinkat (path_fd, new_name, 0) != 0 && errno != ENOENT)
209 printf ("error: unlinkat (\"%s/%s\"): %m\n", path, new_name);
210 exit (1);
213 else
215 /* linkat failed. This is expected if O_EXCL was specified. */
216 if ((flags & O_EXCL) == 0)
218 printf ("error: linkat failed after %s (\"%s\", 0x%x, 0%03o): %m\n",
219 op, path, flags, mode);
220 exit (1);
224 free (proc_fd_path);
225 free (new_name);
226 if (path_fd != AT_FDCWD)
227 close (path_fd);
228 close (fd);
231 /* Check OP/WRAPPER with various flags at a specific PATH and
232 MODE. */
233 static void
234 check_wrapper_mode (const char *op, wrapper_func wrapper,
235 const char *path, mode_t mode)
237 check_wrapper_flags_mode (op, wrapper, path, O_WRONLY, mode);
238 check_wrapper_flags_mode (op, wrapper, path, O_WRONLY | O_EXCL, mode);
239 check_wrapper_flags_mode (op, wrapper, path, O_RDWR, mode);
240 check_wrapper_flags_mode (op, wrapper, path, O_RDWR | O_EXCL, mode);
243 /* Check open/openat with varying permissions. */
244 static void
245 check_wrapper (const char *op, wrapper_func wrapper,
246 const char *path)
248 printf ("info: testing %s at: %s\n", op, path);
249 check_wrapper_mode (op, wrapper, path, 0);
250 check_wrapper_mode (op, wrapper, path, 0640);
251 check_wrapper_mode (op, wrapper, path, 0600);
252 check_wrapper_mode (op, wrapper, path, 0755);
253 check_wrapper_mode (op, wrapper, path, 0750);
256 /* Verify that the directory at PATH supports O_TMPFILE. Exit with
257 status 77 (unsupported) if the kernel does not support O_TMPFILE.
258 Even with kernel support, not all file systems O_TMPFILE, so return
259 true if the directory supports O_TMPFILE, false if not. */
260 static bool
261 probe_path (const char *path)
263 int fd = openat (AT_FDCWD, path, O_TMPFILE | O_RDWR, 0);
264 if (fd < 0)
266 if (errno == EISDIR)
267 /* The system does not support O_TMPFILE. */
269 printf ("info: kernel does not support O_TMPFILE\n");
270 exit (77);
272 if (errno == EOPNOTSUPP)
274 printf ("info: path does not support O_TMPFILE: %s\n", path);
275 return false;
277 printf ("error: openat (\"%s\", O_TMPFILE | O_RDWR): %m\n", path);
278 exit (1);
280 close (fd);
281 return true;
284 static int
285 do_test (void)
287 umask (0);
288 const char *paths[] = { ".", "/dev/shm", "/tmp",
289 getenv ("TEST_TMPFILE_PATH"),
290 NULL };
291 bool supported = false;
292 for (int i = 0; paths[i] != NULL; ++i)
293 if (probe_path (paths[i]))
295 supported = true;
296 check_wrapper ("open", wrap_open, paths[i]);
297 check_wrapper ("openat", wrap_openat, paths[i]);
298 check_wrapper ("open64", wrap_open64, paths[i]);
299 check_wrapper ("openat64", wrap_openat64, paths[i]);
302 if (!supported)
303 return 77;
305 return 0;
308 #else /* !O_TMPFILE */
310 static int
311 do_test (void)
313 return 77;
316 #endif /* O_TMPFILE */
318 #include <support/test-driver.c>