1 /* Test open and openat with O_TMPFILE.
2 Copyright (C) 2016-2023 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. */
31 #include <support/support.h>
34 typedef int (*wrapper_func
) (const char *, int, mode_t
);
36 /* Error-checking wrapper for the open function, compatible with the
39 wrap_open (const char *path
, int flags
, mode_t mode
)
41 int ret
= open (path
, flags
, mode
);
44 printf ("error: open (\"%s\", 0x%x, 0%03o): %m\n", path
, flags
, mode
);
50 /* Error-checking wrapper for the openat function, compatible with the
53 wrap_openat (const char *path
, int flags
, mode_t mode
)
55 int ret
= openat (AT_FDCWD
, path
, flags
, mode
);
58 printf ("error: openat (\"%s\", 0x%x, 0%03o): %m\n", path
, flags
, mode
);
64 /* Error-checking wrapper for the open64 function, compatible with the
67 wrap_open64 (const char *path
, int flags
, mode_t mode
)
69 int ret
= open64 (path
, flags
, mode
);
72 printf ("error: open64 (\"%s\", 0x%x, 0%03o): %m\n", path
, flags
, mode
);
78 /* Error-checking wrapper for the openat64 function, compatible with the
81 wrap_openat64 (const char *path
, int flags
, mode_t mode
)
83 int ret
= openat64 (AT_FDCWD
, path
, flags
, mode
);
86 printf ("error: openat64 (\"%s\", 0x%x, 0%03o): %m\n", path
, flags
, mode
);
92 /* Return true if FD is flagged as deleted in /proc/self/fd, false if
95 is_file_deleted (int fd
)
97 char *proc_fd_path
= xasprintf ("/proc/self/fd/%d", fd
);
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
);
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
);
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
);
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. */
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");
136 ssize_t ret
= read (random_device
, bytes
, sizeof (bytes
));
139 printf ("error: read (\"/dev/urandom\"): %m\n");
142 if (ret
!= sizeof (bytes
))
144 printf ("error: short read from /dev/urandom: %zd\n", ret
);
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. */
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
);
159 if (fstat64 (fd
, &st
) != 0)
161 printf ("error: fstat64: %m\n");
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",
174 /* Check that the file is marked as deleted in /proc. */
175 if (!is_file_deleted (fd
))
177 printf ("error: path in /proc is not marked as deleted\n");
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
186 if (strcmp (path
, ".") == 0)
190 path_fd
= open (path
, O_RDONLY
| O_DIRECTORY
);
193 printf ("error: open (\"%s\"): %m\n", path
);
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
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
);
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
);
226 if (path_fd
!= AT_FDCWD
)
231 /* Check OP/WRAPPER with various flags at a specific PATH and
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. */
245 check_wrapper (const char *op
, wrapper_func wrapper
,
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. */
261 probe_path (const char *path
)
263 int fd
= openat (AT_FDCWD
, path
, O_TMPFILE
| O_RDWR
, 0);
267 /* The system does not support O_TMPFILE. */
269 printf ("info: kernel does not support O_TMPFILE\n");
272 if (errno
== EOPNOTSUPP
)
274 printf ("info: path does not support O_TMPFILE: %s\n", path
);
277 printf ("error: openat (\"%s\", O_TMPFILE | O_RDWR): %m\n", path
);
288 const char *paths
[] = { ".", "/dev/shm", "/tmp",
289 getenv ("TEST_TMPFILE_PATH"),
291 bool supported
= false;
292 for (int i
= 0; paths
[i
] != NULL
; ++i
)
293 if (probe_path (paths
[i
]))
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
]);
308 #else /* !O_TMPFILE */
316 #endif /* O_TMPFILE */
318 #include <support/test-driver.c>