1 /* Test open and openat with O_TMPFILE.
2 Copyright (C) 2016 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 <http://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 static int do_test (void);
33 #define TEST_FUNCTION do_test ()
34 #include "../test-skeleton.c"
37 typedef int (*wrapper_func
) (const char *, int, mode_t
);
39 /* Error-checking wrapper for the open function, compatible with the
42 wrap_open (const char *path
, int flags
, mode_t mode
)
44 int ret
= open (path
, flags
, mode
);
47 printf ("error: open (\"%s\", 0x%x, 0%03o): %m\n", path
, flags
, mode
);
53 /* Error-checking wrapper for the openat function, compatible with the
56 wrap_openat (const char *path
, int flags
, mode_t mode
)
58 int ret
= openat (AT_FDCWD
, path
, flags
, mode
);
61 printf ("error: openat (\"%s\", 0x%x, 0%03o): %m\n", path
, flags
, mode
);
67 /* Error-checking wrapper for the open64 function, compatible with the
70 wrap_open64 (const char *path
, int flags
, mode_t mode
)
72 int ret
= open64 (path
, flags
, mode
);
75 printf ("error: open64 (\"%s\", 0x%x, 0%03o): %m\n", path
, flags
, mode
);
81 /* Error-checking wrapper for the openat64 function, compatible with the
84 wrap_openat64 (const char *path
, int flags
, mode_t mode
)
86 int ret
= openat64 (AT_FDCWD
, path
, flags
, mode
);
89 printf ("error: openat64 (\"%s\", 0x%x, 0%03o): %m\n", path
, flags
, mode
);
95 /* Return true if FD is flagged as deleted in /proc/self/fd, false if
98 is_file_deteted (int fd
)
100 char *proc_fd_path
= xasprintf ("/proc/self/fd/%d", fd
);
101 char file_path
[4096];
102 ssize_t file_path_length
103 = readlink (proc_fd_path
, file_path
, sizeof (file_path
));
104 if (file_path_length
< 0)
106 printf ("error: readlink (\"%s\"): %m", proc_fd_path
);
111 if (file_path_length
== sizeof (file_path
))
113 printf ("error: path in /proc resolves to overlong file name: %.*s\n",
114 (int) file_path_length
, file_path
);
117 const char *deleted
= " (deleted)";
118 if (file_path_length
< strlen (deleted
))
120 printf ("error: path in /proc is too short: %.*s\n",
121 (int) file_path_length
, file_path
);
124 return memcmp (file_path
+ file_path_length
- strlen (deleted
),
125 deleted
, strlen (deleted
)) == 0;
128 /* Obtain a file name which is difficult to guess. */
130 get_random_name (void)
132 unsigned long long bytes
[2];
133 int random_device
= open ("/dev/urandom", O_RDONLY
);
134 if (random_device
< 0)
136 printf ("error: open (\"/dev/urandom\"): %m\n");
139 ssize_t ret
= read (random_device
, bytes
, sizeof (bytes
));
142 printf ("error: read (\"/dev/urandom\"): %m\n");
145 if (ret
!= sizeof (bytes
))
147 printf ("error: short read from /dev/urandom: %zd\n", ret
);
150 close (random_device
);
151 return xasprintf ("tst-open-tmpfile-%08llx%08llx.tmp", bytes
[0], bytes
[1]);
154 /* Check open/openat (as specified by OP and WRAPPER) with a specific
155 PATH/FLAGS/MODE combination. */
157 check_wrapper_flags_mode (const char *op
, wrapper_func wrapper
,
158 const char *path
, int flags
, mode_t mode
)
160 int fd
= wrapper (path
, flags
| O_TMPFILE
, mode
);
162 if (fstat64 (fd
, &st
) != 0)
164 printf ("error: fstat64: %m\n");
168 /* Verify that the mode was correctly processed. */
169 int actual_mode
= st
.st_mode
& 0777;
170 if (actual_mode
!= mode
)
172 printf ("error: unexpected mode; expected 0%03o, actual 0%03o\n",
177 /* Check that the file is marked as deleted in /proc. */
178 if (!is_file_deteted (fd
))
180 printf ("error: path in /proc is not marked as deleted\n");
184 /* Check that the file can be turned into a regular file with
185 linkat. Open a file descriptor for the directory at PATH. Use
186 AT_FDCWD if PATH is ".", to exercise that functionality as
189 if (strcmp (path
, ".") == 0)
193 path_fd
= open (path
, O_RDONLY
| O_DIRECTORY
);
196 printf ("error: open (\"%s\"): %m\n", path
);
201 /* Use a hard-to-guess name for the new directory entry. */
202 char *new_name
= get_random_name ();
204 /* linkat does not require privileges if the path in /proc/self/fd
206 char *proc_fd_path
= xasprintf ("/proc/self/fd/%d", fd
);
207 if (linkat (AT_FDCWD
, proc_fd_path
, path_fd
, new_name
,
208 AT_SYMLINK_FOLLOW
) == 0)
210 if (unlinkat (path_fd
, new_name
, 0) != 0 && errno
!= ENOENT
)
212 printf ("error: unlinkat (\"%s/%s\"): %m\n", path
, new_name
);
218 /* linkat failed. This is expected if O_EXCL was specified. */
219 if ((flags
& O_EXCL
) == 0)
221 printf ("error: linkat failed after %s (\"%s\", 0x%x, 0%03o): %m\n",
222 op
, path
, flags
, mode
);
229 if (path_fd
!= AT_FDCWD
)
234 /* Check OP/WRAPPER with various flags at a specific PATH and
237 check_wrapper_mode (const char *op
, wrapper_func wrapper
,
238 const char *path
, mode_t mode
)
240 check_wrapper_flags_mode (op
, wrapper
, path
, O_WRONLY
, mode
);
241 check_wrapper_flags_mode (op
, wrapper
, path
, O_WRONLY
| O_EXCL
, mode
);
242 check_wrapper_flags_mode (op
, wrapper
, path
, O_RDWR
, mode
);
243 check_wrapper_flags_mode (op
, wrapper
, path
, O_RDWR
| O_EXCL
, mode
);
246 /* Check open/openat with varying permissions. */
248 check_wrapper (const char *op
, wrapper_func wrapper
,
251 printf ("info: testing %s at: %s\n", op
, path
);
252 check_wrapper_mode (op
, wrapper
, path
, 0);
253 check_wrapper_mode (op
, wrapper
, path
, 0640);
254 check_wrapper_mode (op
, wrapper
, path
, 0600);
255 check_wrapper_mode (op
, wrapper
, path
, 0755);
256 check_wrapper_mode (op
, wrapper
, path
, 0750);
259 /* Verify that the directory at PATH supports O_TMPFILE. Exit with
260 status 77 (unsupported) if the kernel does not support O_TMPFILE.
261 Even with kernel support, not all file systems O_TMPFILE, so return
262 true if the directory supports O_TMPFILE, false if not. */
264 probe_path (const char *path
)
266 int fd
= openat (AT_FDCWD
, path
, O_TMPFILE
| O_RDWR
, 0);
270 /* The system does not support O_TMPFILE. */
272 printf ("info: kernel does not support O_TMPFILE\n");
275 if (errno
== EOPNOTSUPP
)
277 printf ("info: path does not support O_TMPFILE: %s\n", path
);
280 printf ("error: openat (\"%s\", O_TMPFILE | O_RDWR): %m\n", path
);
291 const char *paths
[] = { ".", "/dev/shm", "/tmp",
292 getenv ("TEST_TMPFILE_PATH"),
294 bool supported
= false;
295 for (int i
= 0; paths
[i
] != NULL
; ++i
)
296 if (probe_path (paths
[i
]))
299 check_wrapper ("open", wrap_open
, paths
[i
]);
300 check_wrapper ("openat", wrap_openat
, paths
[i
]);
301 check_wrapper ("open64", wrap_open64
, paths
[i
]);
302 check_wrapper ("openat64", wrap_openat64
, paths
[i
]);
311 #else /* !O_TMPFILE */
319 #endif /* O_TMPFILE */