1/******************************************************************************
2 *
3 *   Copyright (c) International Business Machines  Corp., 2006
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 * NAME
20 *      symlinkat01.c
21 *
22 * DESCRIPTION
23 *	This test case will verify basic function of symlinkat
24 *	added by kernel 2.6.16 or up.
25 *
26 * Author
27 *	Yi Yang <yyangcdl@cn.ibm.com>
28 *
29 * History
30 *      08/25/2006      Created first by Yi Yang <yyangcdl@cn.ibm.com>
31 *
32 *****************************************************************************/
33
34#define _GNU_SOURCE
35
36#include <sys/types.h>
37#include <sys/stat.h>
38#include <sys/time.h>
39#include <fcntl.h>
40#include <stdlib.h>
41#include <errno.h>
42#include <string.h>
43#include <signal.h>
44#include "test.h"
45#include "safe_macros.h"
46#include "lapi/syscalls.h"
47
48#define MYRETCODE -999
49#ifndef AT_FDCWD
50#define AT_FDCWD -100
51#endif
52
53struct test_struct;
54static void setup();
55static void cleanup();
56static void setup_every_copy();
57static void mysymlinkat_test(struct test_struct *desc);
58
59#define TEST_DIR1 "olddir"
60#define TEST_DIR2 "newdir"
61#define TEST_DIR3 "deldir"
62#define TEST_FILE1 "oldfile"
63#define TEST_FILE2 "newfile"
64#define TEST_FIFO "fifo"
65
66static char dpathname[256] = "%s/" TEST_DIR2 "/" TEST_FILE1;
67static int olddirfd, newdirfd = -1, cwd_fd = AT_FDCWD, stdinfd = 0, crapfd =
68    -1, deldirfd;
69
70struct test_struct {
71	const char *oldfn;
72	int *newfd;
73	const char *newfn;
74	const char *referencefn1;
75	const char *referencefn2;
76	int expected_errno;
77} test_desc[] = {
78	/* relative paths */
79	{
80	"../" TEST_DIR1 "/" TEST_FILE1, &newdirfd, TEST_FILE1,
81		    TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0},
82	    /* abs path at dst */
83	{
84	"../" TEST_DIR1 "/" TEST_FILE1, &newdirfd, dpathname,
85		    TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0},
86	    /* relative paths to cwd */
87	{
88	"../" TEST_DIR1 "/" TEST_FILE1, &cwd_fd,
89		    TEST_DIR2 "/" TEST_FILE1, TEST_DIR1 "/" TEST_FILE1,
90		    TEST_DIR2 "/" TEST_FILE1, 0},
91	    /* abs path */
92	{
93	"../" TEST_DIR1 "/" TEST_FILE1, &cwd_fd, dpathname,
94		    TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0},
95	    /* relative paths to invalid */
96	{
97	"../" TEST_DIR1 "/" TEST_FILE1, &stdinfd,
98		    TEST_DIR2 "/" TEST_FILE1, 0, 0, ENOTDIR},
99	    /* abs path at dst */
100	{
101	"../" TEST_DIR1 "/" TEST_FILE1, &stdinfd, dpathname,
102		    TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0},
103	    /* relative paths to crap */
104	{
105	"../" TEST_DIR1 "/" TEST_FILE1, &crapfd,
106		    TEST_DIR2 "/" TEST_FILE1, 0, 0, EBADF},
107	    /* abs path at dst */
108	{
109	"../" TEST_DIR1 "/" TEST_FILE1, &crapfd, dpathname,
110		    TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0},
111	    /* relative paths to deleted */
112	{
113	"../" TEST_DIR1 "/" TEST_FILE1, &deldirfd,
114		    TEST_DIR2 "/" TEST_FILE1, 0, 0, ENOENT},
115	    /* abs path at dst */
116	{
117	"../" TEST_DIR1 "/" TEST_FILE1, &deldirfd, dpathname,
118		    TEST_DIR1 "/" TEST_FILE1, TEST_DIR2 "/" TEST_FILE1, 0},
119	    /* fifo link */
120	    /*      { TEST_FIFO, &newdirfd, TEST_FILE1, TEST_DIR1"/"TEST_FIFO, TEST_DIR2"/"TEST_FILE1, 0 }, */
121};
122
123char *TCID = "symlinkat01";
124int TST_TOTAL = sizeof(test_desc) / sizeof(*test_desc);
125
126static int mysymlinkat(const char *oldfilename,
127		       int newdirfd, const char *newfilename)
128{
129	return tst_syscall(__NR_symlinkat, oldfilename, newdirfd, newfilename);
130}
131
132int main(int ac, char **av)
133{
134	int lc;
135	int i;
136
137	tst_parse_opts(ac, av, NULL, NULL);
138
139	setup();
140
141	for (lc = 0; TEST_LOOPING(lc); lc++) {
142
143		tst_count = 0;
144
145		for (i = 0; i < TST_TOTAL; i++) {
146			setup_every_copy();
147			mysymlinkat_test(&test_desc[i]);
148
149		}
150
151	}
152
153	cleanup();
154	tst_exit();
155}
156
157static void setup_every_copy(void)
158{
159	close(newdirfd);
160	unlink(dpathname);
161	rmdir(TEST_DIR2);
162
163	SAFE_MKDIR(cleanup, TEST_DIR2, 0700);
164	newdirfd = SAFE_OPEN(cleanup, TEST_DIR2, O_DIRECTORY);
165}
166
167static void mysymlinkat_test(struct test_struct *desc)
168{
169	int fd;
170
171	TEST(mysymlinkat(desc->oldfn, *desc->newfd, desc->newfn));
172
173	/* check return code */
174	if (TEST_ERRNO == desc->expected_errno) {
175		if (TEST_RETURN == 0 && desc->referencefn1 != NULL) {
176			int tnum = rand(), vnum = ~tnum;
177
178			fd = SAFE_OPEN(cleanup, desc->referencefn1, O_RDWR);
179			SAFE_WRITE(cleanup, SAFE_WRITE_ALL, fd, &tnum,
180				sizeof(tnum));
181			SAFE_CLOSE(cleanup, fd);
182
183			fd = SAFE_OPEN(cleanup, desc->referencefn2, O_RDONLY);
184			SAFE_READ(cleanup, 1, fd, &vnum, sizeof(vnum));
185			SAFE_CLOSE(cleanup, fd);
186
187			if (tnum == vnum)
188				tst_resm(TPASS, "Test passed");
189			else
190				tst_resm(TFAIL,
191					 "The link file's content isn't as same as the original file's "
192					 "although symlinkat returned 0");
193		} else {
194			tst_resm(TPASS,
195				 "symlinkat() returned the expected  errno %d: %s",
196				 TEST_ERRNO, strerror(TEST_ERRNO));
197		}
198	} else {
199		tst_resm(TFAIL,
200			 TEST_RETURN ==
201			 0 ? "symlinkat() surprisingly succeeded" :
202			 "symlinkat() Failed, errno=%d : %s", TEST_ERRNO,
203			 strerror(TEST_ERRNO));
204	}
205}
206
207static void setup(void)
208{
209	char *tmp;
210	int fd;
211
212	tst_sig(NOFORK, DEF_HANDLER, cleanup);
213
214	tst_tmpdir();
215
216	SAFE_MKDIR(cleanup, TEST_DIR1, 0700);
217	SAFE_MKDIR(cleanup, TEST_DIR3, 0700);
218	olddirfd = SAFE_OPEN(cleanup, TEST_DIR1, O_DIRECTORY);
219	deldirfd = SAFE_OPEN(cleanup, TEST_DIR3, O_DIRECTORY);
220	SAFE_RMDIR(cleanup, TEST_DIR3);
221	fd = SAFE_OPEN(cleanup, TEST_DIR1 "/" TEST_FILE1, O_CREAT | O_EXCL, 0600);
222	SAFE_CLOSE(cleanup, fd);
223
224	/* gratuitous memory leak here */
225	tmp = strdup(dpathname);
226	snprintf(dpathname, sizeof(dpathname), tmp, get_current_dir_name());
227
228	TEST_PAUSE;
229}
230
231static void cleanup(void)
232{
233	tst_rmdir();
234}
235