1 /*
2 *
3 * Copyright (c) International Business Machines Corp., 2001
4 * 07/2001 Ported by Wayne Boyer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
14 * the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 /*
21 * Test Description :
22 * Verify that,
23 * 1) readlink(2) returns -1 and sets errno to EACCES if search/write
24 * permission is denied in the directory where the symbolic link
25 * resides.
26 * 2) readlink(2) returns -1 and sets errno to EINVAL if the buffer size
27 * is not positive.
28 * 3) readlink(2) returns -1 and sets errno to EINVAL if the specified
29 * file is not a symbolic link file.
30 * 4) readlink(2) returns -1 and sets errno to ENAMETOOLONG if the
31 * pathname component of symbolic link is too long (ie, > PATH_MAX).
32 * 5) readlink(2) returns -1 and sets errno to ENOENT if the component of
33 * symbolic link points to an empty string.
34 * 6) readlink(2) returns -1 and sets errno to ENOTDIR if a component of
35 * the path prefix is not a directory.
36 * 7) readlink(2) returns -1 and sets errno to ELOOP if too many symbolic
37 * links were encountered in translating the pathname.
38 */
39
40 #include <stdio.h>
41 #include <sys/types.h>
42 #include <sys/fcntl.h>
43 #include <errno.h>
44 #include <string.h>
45 #include <signal.h>
46 #include <sys/stat.h>
47 #include <pwd.h>
48
49 #include "test.h"
50 #include "safe_macros.h"
51
52 #define MODE_RWX (S_IRWXU | S_IRWXG | S_IRWXO)
53 #define FILE_MODE (S_IRUSR | S_IRGRP | S_IROTH)
54 #define DIR_TEMP "testdir_1"
55 #define TEST_FILE1 "testdir_1/tfile_1"
56 #define SYM_FILE1 "testdir_1/sfile_1"
57 #define TEST_FILE2 "tfile_2"
58 #define SYM_FILE2 "sfile_2"
59 #define TEST_FILE3 "tfile_3"
60 #define SYM_FILE3 "tfile_3/sfile_3"
61 #define ELOOPFILE "/test_eloop"
62 #define MAX_SIZE 256
63
64 static char longpathname[PATH_MAX + 2];
65 static char elooppathname[sizeof(ELOOPFILE) * 43] = ".";
66
67 static struct test_case_t {
68 char *link;
69 size_t buf_size;
70 int exp_errno;
71 } test_cases[] = {
72 {SYM_FILE1, 1, EACCES},
73 /* Don't test with bufsize -1, since this cause a fortify-check-fail when
74 using glibc and -D_FORITY_SOURCE=2
75
76 Discussion: http://lkml.org/lkml/2008/10/23/229
77 Conclusion: Only test with 0 as non-positive bufsize.
78
79 { SYM_FILE2, -1, EINVAL, NULL },
80 */
81 {SYM_FILE2, 0, EINVAL},
82 {TEST_FILE2, 1, EINVAL},
83 {longpathname, 1, ENAMETOOLONG},
84 {"", 1, ENOENT},
85 {SYM_FILE3, 1, ENOTDIR},
86 {elooppathname, 1, ELOOP},
87 };
88
89 static void setup(void);
90 static void readlink_verify(struct test_case_t *);
91 static void cleanup(void);
92
93 char *TCID = "readlink03";
94 int TST_TOTAL = ARRAY_SIZE(test_cases);
95
main(int ac,char ** av)96 int main(int ac, char **av)
97 {
98 int i, lc;
99
100 tst_parse_opts(ac, av, NULL, NULL);
101
102 setup();
103
104 for (lc = 0; TEST_LOOPING(lc); lc++) {
105 tst_count = 0;
106
107 for (i = 0; i < TST_TOTAL; i++)
108 readlink_verify(&test_cases[i]);
109 }
110
111 cleanup();
112 tst_exit();
113 }
114
setup(void)115 void setup(void)
116 {
117 struct passwd *ltpuser;
118 int i;
119
120 tst_require_root();
121
122 tst_sig(NOFORK, DEF_HANDLER, cleanup);
123
124 ltpuser = SAFE_GETPWNAM(cleanup, "nobody");
125 SAFE_SETEUID(cleanup, ltpuser->pw_uid);
126
127 TEST_PAUSE;
128
129 tst_tmpdir();
130
131 SAFE_MKDIR(cleanup, DIR_TEMP, MODE_RWX);
132 SAFE_TOUCH(cleanup, TEST_FILE1, 0666, NULL);
133 SAFE_SYMLINK(cleanup, TEST_FILE1, SYM_FILE1);
134 SAFE_CHMOD(cleanup, DIR_TEMP, FILE_MODE);
135
136 SAFE_TOUCH(cleanup, TEST_FILE2, 0666, NULL);
137 SAFE_SYMLINK(cleanup, TEST_FILE2, SYM_FILE2);
138
139 memset(longpathname, 'a', PATH_MAX + 1);
140
141 SAFE_TOUCH(cleanup, TEST_FILE3, 0666, NULL);
142
143 /*
144 * NOTE: the ELOOP test is written based on that the consecutive
145 * symlinks limit in kernel is hardwired to 40.
146 */
147 SAFE_MKDIR(cleanup, "test_eloop", MODE_RWX);
148 SAFE_SYMLINK(cleanup, "../test_eloop", "test_eloop/test_eloop");
149 for (i = 0; i < 43; i++)
150 strcat(elooppathname, ELOOPFILE);
151 }
152
readlink_verify(struct test_case_t * tc)153 void readlink_verify(struct test_case_t *tc)
154 {
155 char buffer[MAX_SIZE];
156
157 if (tc->buf_size == 1)
158 tc->buf_size = sizeof(buffer);
159
160 TEST(readlink(tc->link, buffer, tc->buf_size));
161
162 if (TEST_RETURN != -1) {
163 tst_resm(TFAIL, "readlink() returned %ld, "
164 "expected -1, errno:%d", TEST_RETURN,
165 tc->exp_errno);
166 return;
167 }
168
169 if (TEST_ERRNO == tc->exp_errno) {
170 tst_resm(TPASS | TTERRNO, "readlink() failed as expected");
171 } else {
172 tst_resm(TFAIL | TTERRNO,
173 "readlink() failed unexpectedly; expected: %d - %s",
174 tc->exp_errno, strerror(tc->exp_errno));
175 if (tc->exp_errno == ENOENT && TEST_ERRNO == EINVAL) {
176 tst_resm(TWARN | TTERRNO,
177 "It may be a Kernel Bug, see the patch:"
178 "http://git.kernel.org/linus/1fa1e7f6");
179 }
180 }
181 }
182
cleanup(void)183 void cleanup(void)
184 {
185 if (seteuid(0) == -1)
186 tst_resm(TWARN | TERRNO, "seteuid(0) failed");
187
188 tst_rmdir();
189 }
190