]> sourceware.org Git - glibc.git/blob - io/tst-open-tmpfile.c
tst-open-tmpfile: Add checks for open64, openat64, linkat
[glibc.git] / io / tst-open-tmpfile.c
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.
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 /* This test verifies that open and openat work as expected, i.e. they
20 create a deleted file with the requested file mode. */
21
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>
30
31 static int do_test (void);
32
33 #define TEST_FUNCTION do_test ()
34 #include "../test-skeleton.c"
35
36 #ifdef O_TMPFILE
37 typedef int (*wrapper_func) (const char *, int, mode_t);
38
39 /* Error-checking wrapper for the open function, compatible with the
40 wrapper_func type. */
41 static int
42 wrap_open (const char *path, int flags, mode_t mode)
43 {
44 int ret = open (path, flags, mode);
45 if (ret < 0)
46 {
47 printf ("error: open (\"%s\", 0x%x, 0%03o): %m\n", path, flags, mode);
48 exit (1);
49 }
50 return ret;
51 }
52
53 /* Error-checking wrapper for the openat function, compatible with the
54 wrapper_func type. */
55 static int
56 wrap_openat (const char *path, int flags, mode_t mode)
57 {
58 int ret = openat (AT_FDCWD, path, flags, mode);
59 if (ret < 0)
60 {
61 printf ("error: openat (\"%s\", 0x%x, 0%03o): %m\n", path, flags, mode);
62 exit (1);
63 }
64 return ret;
65 }
66
67 /* Error-checking wrapper for the open64 function, compatible with the
68 wrapper_func type. */
69 static int
70 wrap_open64 (const char *path, int flags, mode_t mode)
71 {
72 int ret = open64 (path, flags, mode);
73 if (ret < 0)
74 {
75 printf ("error: open64 (\"%s\", 0x%x, 0%03o): %m\n", path, flags, mode);
76 exit (1);
77 }
78 return ret;
79 }
80
81 /* Error-checking wrapper for the openat64 function, compatible with the
82 wrapper_func type. */
83 static int
84 wrap_openat64 (const char *path, int flags, mode_t mode)
85 {
86 int ret = openat64 (AT_FDCWD, path, flags, mode);
87 if (ret < 0)
88 {
89 printf ("error: openat64 (\"%s\", 0x%x, 0%03o): %m\n", path, flags, mode);
90 exit (1);
91 }
92 return ret;
93 }
94
95 /* Return true if FD is flagged as deleted in /proc/self/fd, false if
96 not. */
97 static bool
98 is_file_deteted (int fd)
99 {
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)
105 {
106 printf ("error: readlink (\"%s\"): %m", proc_fd_path);
107 free (proc_fd_path);
108 exit (1);
109 }
110 free (proc_fd_path);
111 if (file_path_length == sizeof (file_path))
112 {
113 printf ("error: path in /proc resolves to overlong file name: %.*s\n",
114 (int) file_path_length, file_path);
115 exit (1);
116 }
117 const char *deleted = " (deleted)";
118 if (file_path_length < strlen (deleted))
119 {
120 printf ("error: path in /proc is too short: %.*s\n",
121 (int) file_path_length, file_path);
122 exit (1);
123 }
124 return memcmp (file_path + file_path_length - strlen (deleted),
125 deleted, strlen (deleted)) == 0;
126 }
127
128 /* Obtain a file name which is difficult to guess. */
129 static char *
130 get_random_name (void)
131 {
132 unsigned long long bytes[2];
133 int random_device = open ("/dev/urandom", O_RDONLY);
134 if (random_device < 0)
135 {
136 printf ("error: open (\"/dev/urandom\"): %m\n");
137 exit (1);
138 }
139 ssize_t ret = read (random_device, bytes, sizeof (bytes));
140 if (ret < 0)
141 {
142 printf ("error: read (\"/dev/urandom\"): %m\n");
143 exit (1);
144 }
145 if (ret != sizeof (bytes))
146 {
147 printf ("error: short read from /dev/urandom: %zd\n", ret);
148 exit (1);
149 }
150 close (random_device);
151 return xasprintf ("tst-open-tmpfile-%08llx%08llx.tmp", bytes[0], bytes[1]);
152 }
153
154 /* Check open/openat (as specified by OP and WRAPPER) with a specific
155 PATH/FLAGS/MODE combination. */
156 static void
157 check_wrapper_flags_mode (const char *op, wrapper_func wrapper,
158 const char *path, int flags, mode_t mode)
159 {
160 int fd = wrapper (path, flags | O_TMPFILE, mode);
161 struct stat64 st;
162 if (fstat64 (fd, &st) != 0)
163 {
164 printf ("error: fstat64: %m\n");
165 exit (1);
166 }
167
168 /* Verify that the mode was correctly processed. */
169 int actual_mode = st.st_mode & 0777;
170 if (actual_mode != mode)
171 {
172 printf ("error: unexpected mode; expected 0%03o, actual 0%03o\n",
173 mode, actual_mode);
174 exit (1);
175 }
176
177 /* Check that the file is marked as deleted in /proc. */
178 if (!is_file_deteted (fd))
179 {
180 printf ("error: path in /proc is not marked as deleted\n");
181 exit (1);
182 }
183
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
187 well. */
188 int path_fd;
189 if (strcmp (path, ".") == 0)
190 path_fd = AT_FDCWD;
191 else
192 {
193 path_fd = open (path, O_RDONLY | O_DIRECTORY);
194 if (path_fd < 0)
195 {
196 printf ("error: open (\"%s\"): %m\n", path);
197 exit (1);
198 }
199 }
200
201 /* Use a hard-to-guess name for the new directory entry. */
202 char *new_name = get_random_name ();
203
204 /* linkat does not require privileges if the path in /proc/self/fd
205 is used. */
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)
209 {
210 if (unlinkat (path_fd, new_name, 0) != 0 && errno != ENOENT)
211 {
212 printf ("error: unlinkat (\"%s/%s\"): %m\n", path, new_name);
213 exit (1);
214 }
215 }
216 else
217 {
218 /* linkat failed. This is expected if O_EXCL was specified. */
219 if ((flags & O_EXCL) == 0)
220 {
221 printf ("error: linkat failed after %s (\"%s\", 0x%x, 0%03o): %m\n",
222 op, path, flags, mode);
223 exit (1);
224 }
225 }
226
227 free (proc_fd_path);
228 free (new_name);
229 if (path_fd != AT_FDCWD)
230 close (path_fd);
231 close (fd);
232 }
233
234 /* Check OP/WRAPPER with various flags at a specific PATH and
235 MODE. */
236 static void
237 check_wrapper_mode (const char *op, wrapper_func wrapper,
238 const char *path, mode_t mode)
239 {
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);
244 }
245
246 /* Check open/openat with varying permissions. */
247 static void
248 check_wrapper (const char *op, wrapper_func wrapper,
249 const char *path)
250 {
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);
257 }
258
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. */
263 static bool
264 probe_path (const char *path)
265 {
266 int fd = openat (AT_FDCWD, path, O_TMPFILE | O_RDWR, 0);
267 if (fd < 0)
268 {
269 if (errno == EISDIR)
270 /* The system does not support O_TMPFILE. */
271 {
272 printf ("info: kernel does not support O_TMPFILE\n");
273 exit (77);
274 }
275 if (errno == EOPNOTSUPP)
276 {
277 printf ("info: path does not support O_TMPFILE: %s\n", path);
278 return false;
279 }
280 printf ("error: openat (\"%s\", O_TMPFILE | O_RDWR): %m\n", path);
281 exit (1);
282 }
283 close (fd);
284 return true;
285 }
286
287 static int
288 do_test (void)
289 {
290 umask (0);
291 const char *paths[] = { ".", "/dev/shm", "/tmp",
292 getenv ("TEST_TMPFILE_PATH"),
293 NULL };
294 bool supported = false;
295 for (int i = 0; paths[i] != NULL; ++i)
296 if (probe_path (paths[i]))
297 {
298 supported = true;
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]);
303 }
304
305 if (!supported)
306 return 77;
307
308 return 0;
309 }
310
311 #else /* !O_TMPFILE */
312
313 static int
314 do_test (void)
315 {
316 return 77;
317 }
318
319 #endif /* O_TMPFILE */
This page took 0.055058 seconds and 6 git commands to generate.