1/*
2 * Copyright (c) 2014 Fujitsu Ltd.
3 * Author: Zeng Linggang <zenglg.jy@cn.fujitsu.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program.
15 */
16/*
17 * DESCRIPTION
18 *	This test case will verify basic function of open(2) with the flags
19 *	O_APPEND, O_NOATIME, O_CLOEXEC and O_LARGEFILE.
20 */
21
22#define _GNU_SOURCE
23
24#include <stdio.h>
25#include <sys/types.h>
26#include <sys/wait.h>
27#include <sys/mount.h>
28#include <unistd.h>
29#include <mntent.h>
30#include <errno.h>
31#include "test.h"
32#include "safe_macros.h"
33#include "lapi/fcntl.h"
34#include "lapi/mount.h"
35
36#define MNTPOINT	"mntpoint"
37#define TEST_FILE	MNTPOINT"/test_file"
38#define LARGE_FILE	"large_file"
39
40#define DIR_MODE 0755
41
42char *TCID = "open12";
43
44static const char *device;
45static unsigned int mount_flag, skip_noatime;
46
47static void setup(void);
48static void cleanup(void);
49static void test_append(void);
50static void test_noatime(void);
51static void test_cloexec(void);
52static void test_largefile(void);
53
54static void (*test_func[])(void) = { test_append, test_noatime, test_cloexec,
55				     test_largefile };
56
57int TST_TOTAL = ARRAY_SIZE(test_func);
58
59int main(int argc, char **argv)
60{
61	int lc;
62	int i;
63
64	tst_parse_opts(argc, argv, NULL, NULL);
65
66	setup();
67
68	for (lc = 0; TEST_LOOPING(lc); lc++) {
69		tst_count = 0;
70		for (i = 0; i < TST_TOTAL; i++)
71			(*test_func[i])();
72	}
73
74	cleanup();
75	tst_exit();
76}
77
78static void setup(void)
79{
80	const char *mount_flags[] = {"noatime", "relatime", NULL};
81
82	TEST_PAUSE;
83
84	tst_sig(FORK, DEF_HANDLER, cleanup);
85
86	tst_tmpdir();
87
88	SAFE_MKDIR(cleanup, MNTPOINT, DIR_MODE);
89
90	if (tst_path_has_mnt_flags(cleanup, NULL, mount_flags)) {
91		const char *fs_type;
92
93		if ((tst_kvercmp(2, 6, 30)) < 0) {
94			tst_resm(TCONF,
95				"MS_STRICTATIME flags for mount(2) needs kernel 2.6.30 "
96				"or higher");
97			skip_noatime = 1;
98			return;
99		}
100
101		fs_type = tst_dev_fs_type();
102		device = tst_acquire_device(cleanup);
103
104		if (!device) {
105			tst_resm(TINFO, "Failed to obtain block device");
106			skip_noatime = 1;
107			goto end;
108		}
109
110		tst_mkfs(cleanup, device, fs_type, NULL, NULL);
111
112		SAFE_MOUNT(cleanup, device, MNTPOINT, fs_type, MS_STRICTATIME, NULL);
113		mount_flag = 1;
114	}
115
116end:
117	SAFE_FILE_PRINTF(cleanup, TEST_FILE, TEST_FILE);
118}
119
120static void test_append(void)
121{
122	off_t len1, len2;
123
124	TEST(open(TEST_FILE, O_RDWR | O_APPEND, 0777));
125
126	if (TEST_RETURN == -1) {
127		tst_resm(TFAIL | TTERRNO, "open failed");
128		return;
129	}
130
131	len1 = SAFE_LSEEK(cleanup, TEST_RETURN, 0, SEEK_CUR);
132	SAFE_WRITE(cleanup, 1, TEST_RETURN, TEST_FILE, sizeof(TEST_FILE));
133	len2 = SAFE_LSEEK(cleanup, TEST_RETURN, 0, SEEK_CUR);
134	SAFE_CLOSE(cleanup, TEST_RETURN);
135
136	if (len2 > len1)
137		tst_resm(TPASS, "test O_APPEND for open success");
138	else
139		tst_resm(TFAIL, "test O_APPEND for open failed");
140}
141
142static void test_noatime(void)
143{
144	char read_buf;
145	struct stat old_stat, new_stat;
146
147	if ((tst_kvercmp(2, 6, 8)) < 0) {
148		tst_resm(TCONF,
149			 "O_NOATIME flags test for open(2) needs kernel 2.6.8 "
150			 "or higher");
151		return;
152	}
153
154	if (skip_noatime) {
155		tst_resm(TCONF,
156		         "test O_NOATIME flag for open needs filesystems which "
157		         "is mounted without noatime and relatime");
158		return;
159	}
160
161	SAFE_STAT(cleanup, TEST_FILE, &old_stat);
162
163	sleep(1);
164
165	TEST(open(TEST_FILE, O_RDONLY | O_NOATIME, 0777));
166
167	if (TEST_RETURN == -1) {
168		tst_resm(TFAIL | TTERRNO, "open failed");
169		return;
170	}
171	SAFE_READ(cleanup, 1, TEST_RETURN, &read_buf, 1);
172	SAFE_CLOSE(cleanup, TEST_RETURN);
173	SAFE_STAT(cleanup, TEST_FILE, &new_stat);
174
175	if (old_stat.st_atime == new_stat.st_atime)
176		tst_resm(TPASS, "test O_NOATIME for open success");
177	else
178		tst_resm(TFAIL, "test O_NOATIME for open failed");
179}
180
181static void test_cloexec(void)
182{
183	pid_t pid;
184	int status;
185	char buf[20];
186
187	if ((tst_kvercmp(2, 6, 23)) < 0) {
188		tst_resm(TCONF,
189			 "O_CLOEXEC flags test for open(2) needs kernel 2.6.23 "
190			 "or higher");
191		return;
192	}
193
194	TEST(open(TEST_FILE, O_RDWR | O_APPEND | O_CLOEXEC, 0777));
195
196	if (TEST_RETURN == -1) {
197		tst_resm(TFAIL | TTERRNO, "open failed");
198		return;
199	}
200
201	sprintf(buf, "%ld", TEST_RETURN);
202
203	pid = tst_fork();
204	if (pid < 0)
205		tst_brkm(TBROK | TERRNO, cleanup, "fork() failed");
206
207	if (pid == 0) {
208		if (execlp("open12_child", "open12_child", buf, NULL))
209			exit(2);
210	}
211
212	SAFE_CLOSE(cleanup, TEST_RETURN);
213
214	if (wait(&status) != pid)
215		tst_brkm(TBROK | TERRNO, cleanup, "wait() failed");
216
217	if (WIFEXITED(status)) {
218		switch ((int8_t)WEXITSTATUS(status)) {
219		case 0:
220			tst_resm(TPASS, "test O_CLOEXEC for open success");
221		break;
222		case 1:
223			tst_resm(TFAIL, "test O_CLOEXEC for open failed");
224		break;
225		default:
226			tst_brkm(TBROK, cleanup, "execlp() failed");
227		}
228	} else {
229		tst_brkm(TBROK, cleanup,
230				 "open12_child exits with unexpected error");
231	}
232}
233
234static void test_largefile(void)
235{
236	int fd;
237	off64_t offset;
238
239	fd = SAFE_OPEN(cleanup, LARGE_FILE,
240				O_LARGEFILE | O_RDWR | O_CREAT, 0777);
241
242	offset = lseek64(fd, 4.1*1024*1024*1024, SEEK_SET);
243	if (offset == -1)
244		tst_brkm(TBROK | TERRNO, cleanup, "lseek64 failed");
245
246	SAFE_WRITE(cleanup, 1, fd, LARGE_FILE, sizeof(LARGE_FILE));
247
248	SAFE_CLOSE(cleanup, fd);
249
250	TEST(open(LARGE_FILE, O_LARGEFILE | O_RDONLY, 0777));
251
252	if (TEST_RETURN == -1) {
253		tst_resm(TFAIL, "test O_LARGEFILE for open failed");
254	} else {
255		tst_resm(TPASS, "test O_LARGEFILE for open success");
256		SAFE_CLOSE(cleanup, TEST_RETURN);
257	}
258}
259
260static void cleanup(void)
261{
262	if (mount_flag && tst_umount(MNTPOINT) == -1)
263		tst_brkm(TWARN | TERRNO, NULL, "umount(2) failed");
264
265	if (device)
266		tst_release_device(device);
267
268	tst_rmdir();
269}
270