1/*
2 * Copyright (c) 2015-2016 Oracle and/or its affiliates. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Author: Alexey Kodanev <alexey.kodanev@oracle.com>
18 *
19 */
20
21#define _GNU_SOURCE
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <unistd.h>
25#include <fcntl.h>
26#include <errno.h>
27
28#include "test.h"
29#include "safe_macros.h"
30#include "lapi/fcntl.h"
31#include "openat.h"
32
33char *TCID = "openat03";
34int TST_TOTAL = 3;
35static ssize_t size;
36static char buf[1024];
37static const ssize_t blocks_num = 4;
38static struct stat st;
39
40static void cleanup(void)
41{
42	tst_rmdir();
43}
44
45static void setup(void)
46{
47	tst_tmpdir();
48
49	size = sizeof(buf);
50
51	memset(buf, 1, size);
52
53	int fd = openat(AT_FDCWD, ".", O_TMPFILE | O_RDWR, 0600);
54
55	if (fd == -1) {
56		if (errno == EISDIR || errno == ENOTSUP)
57			tst_brkm(TCONF, cleanup, "O_TMPFILE not supported");
58
59		tst_brkm(TBROK | TERRNO, cleanup, "openat() failed");
60	}
61
62	SAFE_CLOSE(cleanup, fd);
63}
64
65static int openat_tmp(int mode)
66{
67	int fd = openat(AT_FDCWD, ".", O_TMPFILE | O_RDWR, mode);
68
69	if (fd >= 0)
70		return fd;
71
72	tst_brkm(TBROK | TERRNO, cleanup, "openat() failed");
73}
74
75static void write_file(int fd)
76{
77	int i;
78
79	for (i = 0; i < blocks_num; ++i)
80		SAFE_WRITE(cleanup, 1, fd, buf, size);
81}
82
83void test01(void)
84{
85	int fd;
86	char path[PATH_MAX], tmp[PATH_MAX];
87
88	tst_resm(TINFO, "creating a file with O_TMPFILE flag");
89	fd = openat_tmp(0600);
90
91	tst_resm(TINFO, "writing data to the file");
92	write_file(fd);
93
94	SAFE_FSTAT(cleanup, fd, &st);
95	tst_resm(TINFO, "file size is '%zu'", st.st_size);
96
97	if (st.st_size != blocks_num * size) {
98		tst_resm(TFAIL, "not expected size: '%zu' != '%zu'",
99			 st.st_size, blocks_num * size);
100		SAFE_CLOSE(cleanup, fd);
101		return;
102	}
103
104	tst_resm(TINFO, "looking for the file in '.'");
105	if (!tst_dir_is_empty(cleanup, ".", 1))
106		tst_brkm(TFAIL, cleanup, "found a file, this is not expected");
107	tst_resm(TINFO, "file not found, OK");
108
109	snprintf(path, PATH_MAX,  "/proc/self/fd/%d", fd);
110	SAFE_READLINK(cleanup, path, tmp, PATH_MAX);
111
112	tst_resm(TINFO, "renaming '%s' -> 'tmpfile'", tmp);
113	SAFE_LINKAT(cleanup, AT_FDCWD, path, AT_FDCWD, "tmpfile",
114		    AT_SYMLINK_FOLLOW);
115
116	if (tst_dir_is_empty(cleanup, ".", 1))
117		tst_brkm(TFAIL, cleanup, "file not found");
118
119	SAFE_UNLINK(cleanup, "tmpfile");
120	SAFE_CLOSE(cleanup, fd);
121
122	tst_resm(TPASS, "single file tests passed");
123}
124
125static void read_file(int fd)
126{
127	int i;
128	char tmp[size];
129
130	SAFE_LSEEK(cleanup, fd, 0, SEEK_SET);
131
132	for (i = 0; i < blocks_num; ++i) {
133		SAFE_READ(cleanup, 0, fd, tmp, size);
134		if (memcmp(buf, tmp, size))
135			tst_brkm(TFAIL, cleanup, "got unexepected data");
136	}
137}
138
139static void test02(void)
140{
141	const int files_num = 100;
142	int i, fd[files_num];
143	char path[PATH_MAX];
144
145	tst_resm(TINFO, "create files in multiple directories");
146	for (i = 0; i < files_num; ++i) {
147		snprintf(path, PATH_MAX, "tst02_%d", i);
148		SAFE_MKDIR(cleanup, path, 0700);
149		SAFE_CHDIR(cleanup, path);
150
151		fd[i] = openat_tmp(0600);
152	}
153
154	tst_resm(TINFO, "removing test directories");
155	for (i = files_num - 1; i >= 0; --i) {
156		SAFE_CHDIR(cleanup, "..");
157		snprintf(path, PATH_MAX, "tst02_%d", i);
158		SAFE_RMDIR(cleanup, path);
159	}
160
161	tst_resm(TINFO, "writing/reading temporary files");
162	for (i = 0; i < files_num; ++i) {
163		write_file(fd[i]);
164		read_file(fd[i]);
165	}
166
167	tst_resm(TINFO, "closing temporary files");
168	for (i = 0; i < files_num; ++i)
169		SAFE_CLOSE(cleanup, fd[i]);
170
171	tst_resm(TPASS, "multiple files tests passed");
172}
173
174static void link_tmp_file(int fd)
175{
176	char path1[PATH_MAX], path2[PATH_MAX];
177
178	snprintf(path1, PATH_MAX,  "/proc/self/fd/%d", fd);
179	snprintf(path2, PATH_MAX,  "tmpfile_%d", fd);
180
181	SAFE_LINKAT(cleanup, AT_FDCWD, path1, AT_FDCWD, path2,
182		    AT_SYMLINK_FOLLOW);
183}
184
185static void test03(void)
186{
187	const int files_num = 100;
188	const mode_t test_perms[] = { 0, 07777, 001, 0755, 0644, 0440 };
189
190	int i, fd[files_num];
191	char path[PATH_MAX];
192	struct stat st;
193	mode_t mask = umask(0), perm;
194
195	umask(mask);
196
197	tst_resm(TINFO, "create multiple directories, link files into them");
198	tst_resm(TINFO, "and check file permissions");
199	for (i = 0; i < files_num; ++i) {
200
201		snprintf(path, PATH_MAX, "tst03_%d", i);
202		SAFE_MKDIR(cleanup, path, 0700);
203		SAFE_CHDIR(cleanup, path);
204
205		perm = test_perms[i % ARRAY_SIZE(test_perms)];
206
207		fd[i] = openat_tmp(perm);
208
209		write_file(fd[i]);
210		read_file(fd[i]);
211
212		link_tmp_file(fd[i]);
213
214		snprintf(path, PATH_MAX, "tmpfile_%d", fd[i]);
215
216		SAFE_LSTAT(cleanup, path, &st);
217
218		mode_t exp_mode = perm & ~mask;
219
220		if ((st.st_mode & ~S_IFMT) != exp_mode) {
221			tst_brkm(TFAIL, cleanup,
222				"file mode read %o, but expected %o",
223				st.st_mode & ~S_IFMT, exp_mode);
224		}
225	}
226
227	tst_resm(TINFO, "remove files, directories");
228	for (i = files_num - 1; i >= 0; --i) {
229		snprintf(path, PATH_MAX, "tmpfile_%d", fd[i]);
230		SAFE_UNLINK(cleanup, path);
231		SAFE_CLOSE(cleanup, fd[i]);
232
233		SAFE_CHDIR(cleanup, "..");
234
235		snprintf(path, PATH_MAX, "tst03_%d", i);
236		SAFE_RMDIR(cleanup, path);
237	}
238
239	tst_resm(TPASS, "file permission tests passed");
240}
241
242int main(int ac, char *av[])
243{
244	int lc;
245
246	tst_parse_opts(ac, av, NULL, NULL);
247
248	setup();
249
250	for (lc = 0; TEST_LOOPING(lc); ++lc) {
251		tst_count = 0;
252		test01();
253		test02();
254		test03();
255	}
256
257	cleanup();
258	tst_exit();
259}
260