Home | History | Annotate | Download | only in readlink
      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 
     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 
    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 
    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 
    183 void cleanup(void)
    184 {
    185 	if (seteuid(0) == -1)
    186 		tst_resm(TWARN | TERRNO, "seteuid(0) failed");
    187 
    188 	tst_rmdir();
    189 }
    190