1/*
2 *
3 *   Copyright (c) International Business Machines  Corp., 2001
4 *
5 *   This program is free software;  you can redistribute it and/or modify
6 *   it under the terms of the GNU General Public License as published by
7 *   the Free Software Foundation; either version 2 of the License, or
8 *   (at your option) any later version.
9 *
10 *   This program 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
13 *   the GNU General Public License for more details.
14 *
15 *   You should have received a copy of the GNU General Public License
16 *   along with this program;  if not, write to the Free Software
17 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20/*
21 * Test Name: mknod06
22 *
23 * Test Description:
24 * Verify that,
25 *   1) mknod(2) returns -1 and sets errno to EEXIST if specified path
26 *	already exists.
27 *   2) mknod(2) returns -1 and sets errno to EFAULT if pathname points
28 *	outside user's accessible address space.
29 *   3) mknod(2) returns -1 and sets errno to ENOENT if the directory
30 *	component in pathname does not exist.
31 *   4) mknod(2) returns -1 and sets errno to ENAMETOOLONG if the pathname
32 *	component was too long.
33 *   5) mknod(2) returns -1 and sets errno to ENOTDIR if the directory
34 *	component in pathname is not a directory.
35 *
36 * Expected Result:
37 *  mknod() should fail with return value -1 and set expected errno.
38 *
39 * Algorithm:
40 *  Setup:
41 *   Setup signal handling.
42 *   Create temporary directory.
43 *   Pause for SIGUSR1 if option specified.
44 *
45 *  Test:
46 *   Loop if the proper options are given.
47 *   Execute system call
48 *   Check return code, if system call failed (return=-1)
49 *   	if errno set == expected errno
50 *   		Issue sys call fails with expected return value and errno.
51 *   	Otherwise,
52 *		Issue sys call fails with unexpected errno.
53 *   Otherwise,
54 *	Issue sys call returns unexpected value.
55 *
56 *  Cleanup:
57 *   Print errno log and/or timing stats if options given
58 *   Delete the temporary directory(s)/file(s) created.
59 *
60 * Usage:  <for command-line>
61 *  mknod06 [-c n] [-e] [-i n] [-I x] [-P x] [-t]
62 *     where,  -c n : Run n copies concurrently.
63 *             -e   : Turn on errno logging.
64 *	       -i n : Execute test n times.
65 *	       -I x : Execute test for x seconds.
66 *	       -P x : Pause for x seconds between iterations.
67 *	       -t   : Turn on syscall timing.
68 *
69 * HISTORY
70 *	07/2001 Ported by Wayne Boyer
71 *
72 * RESTRICTIONS:
73 *  This test should be executed by super-user (root) only.
74 */
75
76#include <stdio.h>
77#include <stdlib.h>
78#include <unistd.h>
79#include <errno.h>
80#include <string.h>
81#include <signal.h>
82#include <sys/types.h>
83#include <sys/stat.h>
84#include <sys/param.h>
85#include <sys/mman.h>
86
87#include "test.h"
88
89#define MODE_RWX		S_IFIFO | S_IRWXU | S_IRWXG | S_IRWXO
90
91int setup1();			/* setup function to test mknod for EEXIST */
92int setup3();			/* setup function to test mknod for ENOTDIR */
93int longpath_setup();		/* setup function to test mknod for ENAMETOOLONG */
94int no_setup();			/* simply returns 0 to the caller */
95char Longpathname[PATH_MAX + 2];
96char High_address_node[64];
97
98struct test_case_t {		/* test case struct. to hold ref. test cond's */
99	char *pathname;
100	char *desc;
101	int exp_errno;
102	int (*setupfunc) ();
103} Test_cases[] = {
104	{
105	"tnode_1", "Specified node already exists", EEXIST, setup1},
106#if !defined(UCLINUX)
107	{
108	(char *)-1, "Negative address", EFAULT, no_setup}, {
109	High_address_node, "Address beyond address space", EFAULT,
110		    no_setup},
111#endif
112	{
113	"testdir_2/tnode_2", "Non-existent file", ENOENT, no_setup}, {
114	"", "Pathname is empty", ENOENT, no_setup}, {
115	Longpathname, "Pathname too long", ENAMETOOLONG, longpath_setup}, {
116	"tnode/tnode_3", "Path contains regular file", ENOTDIR, setup3}, {
117	NULL, NULL, 0, no_setup}
118};
119
120char *TCID = "mknod06";
121int TST_TOTAL = ARRAY_SIZE(Test_cases);
122#if !defined(UCLINUX)
123extern char *get_high_address();
124#else
125#endif
126
127char *bad_addr = 0;
128
129void setup();			/* setup function for the tests */
130void cleanup();			/* cleanup function for the tests */
131
132int main(int ac, char **av)
133{
134	int lc;
135	char *node_name;	/* ptr. for node name created */
136	char *test_desc;	/* test specific error message */
137	int ind;		/* counter to test different test conditions */
138
139	tst_parse_opts(ac, av, NULL, NULL);
140
141	/*
142	 * Invoke setup function to call individual test setup functions
143	 * for the test which run as root/super-user.
144	 */
145	setup();
146
147	for (lc = 0; TEST_LOOPING(lc); lc++) {
148
149		tst_count = 0;
150
151		for (ind = 0; Test_cases[ind].desc != NULL; ind++) {
152			node_name = Test_cases[ind].pathname;
153			test_desc = Test_cases[ind].desc;
154
155#if !defined(UCLINUX)
156			if (node_name == High_address_node) {
157				node_name = get_high_address();
158			}
159#endif
160
161			/*
162			 * Call mknod(2) to test different test conditions.
163			 * verify that it fails with -1 return value and
164			 * sets appropriate errno.
165			 */
166			TEST(mknod(node_name, MODE_RWX, 0));
167
168			/* Check return code from mknod(2) */
169			if (TEST_RETURN != -1) {
170				tst_resm(TFAIL,
171					 "mknod() returned %ld, expected "
172					 "-1, errno:%d", TEST_RETURN,
173					 Test_cases[ind].exp_errno);
174				continue;
175			}
176
177			if (TEST_ERRNO == Test_cases[ind].exp_errno) {
178				tst_resm(TPASS, "mknod() fails, %s, errno:%d",
179					 test_desc, TEST_ERRNO);
180			} else {
181				tst_resm(TFAIL, "mknod() fails, %s, errno:%d, "
182					 "expected errno:%d", test_desc,
183					 TEST_ERRNO, Test_cases[ind].exp_errno);
184			}
185		}
186
187	}
188
189	/*
190	 * Invoke cleanup() to delete the test directories created
191	 * in the setup().
192	 */
193	cleanup();
194
195	tst_exit();
196}
197
198/*
199 * setup(void) - performs all ONE TIME setup for this test.
200 * 	Exit the test program on receipt of unexpected signals.
201 *	Create a temporary directory used to hold test directories and nodes
202 *	created and change the directory to it.
203 *	Invoke individual test setup functions according to the order
204 *	set in struct. definition.
205 */
206void setup(void)
207{
208	int ind;
209
210	tst_require_root();
211
212	/* Capture unexpected signals */
213	tst_sig(NOFORK, DEF_HANDLER, cleanup);
214
215	TEST_PAUSE;
216
217	/* Make a temp dir and cd to it */
218	tst_tmpdir();
219
220#if !defined(UCLINUX)
221	bad_addr = mmap(0, 1, PROT_NONE,
222			MAP_PRIVATE_EXCEPT_UCLINUX | MAP_ANONYMOUS, 0, 0);
223	if (bad_addr == MAP_FAILED) {
224		tst_brkm(TBROK, cleanup, "mmap failed");
225	}
226	Test_cases[2].pathname = bad_addr;
227#endif
228	/* call individual setup functions */
229	for (ind = 0; Test_cases[ind].desc != NULL; ind++) {
230		Test_cases[ind].setupfunc();
231	}
232}
233
234/*
235 * no_setup() - Some test conditions for mknod(2) do not any setup.
236 *		Hence, this function just returns 0.
237 */
238int no_setup(void)
239{
240	return 0;
241}
242
243/*
244 * longpath_setup() - setup to create a node with a name length exceeding
245 *		      the MAX. length of PATH_MAX.
246 *   This function retruns 0.
247 */
248int longpath_setup(void)
249{
250	int ind;		/* counter variable */
251
252	for (ind = 0; ind <= (PATH_MAX + 1); ind++) {
253		Longpathname[ind] = 'a';
254	}
255	return 0;
256}
257
258/*
259 * setup1() - setup function for a test condition for which mknod(2)
260 *	      returns -1 and sets errno to EEXIST.
261 *  This function creates a node using mknod(2) and tries to create
262 *  same node in the test and fails with above errno.
263 *  This function returns 0.
264 */
265int setup1(void)
266{
267	/* Create a node using mknod */
268	if (mknod("tnode_1", MODE_RWX, 0) < 0) {
269		tst_brkm(TBROK, cleanup, "Fails to create node in setup1()");
270	}
271
272	return 0;
273}
274
275/*
276 * setup3() - setup function for a test condition for which mknod(2)
277 *	      returns -1 and sets errno to ENOTDIR.
278 *  This function creates a node under temporary directory and the
279 *  test attempts to create another node under under this node and fails
280 *  with ENOTDIR as the node created here is a regular file.
281 *  This function returns 0.
282 */
283int setup3(void)
284{
285	/* Create a node using mknod */
286	if (mknod("tnode", MODE_RWX, 0) < 0) {
287		tst_brkm(TBROK, cleanup, "Fails to create node in setup3()");
288	}
289
290	return 0;
291}
292
293/*
294 * cleanup() - Performs all ONE TIME cleanup for this test at
295 *             completion or premature exit.
296 *	Print test timing stats and errno log if test executed with options.
297 *	Remove temporary directory and sub-directories/files under it
298 *	created during setup().
299 *	Exit the test program with normal exit code.
300 */
301void cleanup(void)
302{
303
304	tst_rmdir();
305
306}
307