1/*	$OpenBSD: realpathtest.c,v 1.13 2019/08/06 11:38:16 bluhm Exp $ */
2
3/*
4 * Copyright (c) 2019 Bob Beck <beck@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/stat.h>
21
22#include <stdio.h>
23#include <limits.h>
24#include <stdlib.h>
25#include <string.h>
26#include <unistd.h>
27#include <err.h>
28#include <errno.h>
29
30/*
31 * Reference copy of userland realpath(3) implementation.
32 * Assumed to be correct.
33 */
34extern char *realpath3(const char *pathname, char *resolved);
35
36struct rp_compare {
37	char * resolv2;
38	char * resolv3;
39	char * r2;
40	char * r3;
41	int e2;
42	int e3;
43};
44
45static struct rp_compare
46rpcompare(const char *pathname, char *resolv2,
47    char *resolv3) {
48	struct rp_compare ret = {0};
49
50	errno = 0;
51	ret.r2 = realpath(pathname, resolv2);
52	ret.e2 = errno;
53	ret.resolv2 = resolv2;
54	errno = 0;
55	ret.r3 = realpath3(pathname, resolv3);
56	ret.e3 = errno;
57	ret.resolv3 = resolv3;
58	errno = 0;
59	return ret;
60}
61
62#define RP_SHOULD_SUCCEED(A, B, C) do {					\
63	struct rp_compare rc = rpcompare(A, B, C);			\
64	if (rc.r2 == NULL)  {						\
65		errno = rc.e2;						\
66		err(1, "%s:%d - realpath of '%s' failed", __FILE__,	\
67		    __LINE__, (A));					\
68	}								\
69	if (rc.r3 == NULL)  {						\
70		errno = rc.e3;						\
71		err(1, "%s:%d - realpath3 of '%s' failed", __FILE__,	\
72		    __LINE__, (A));					\
73	}								\
74	if (strcmp(rc.r2, rc.r3) != 0)					\
75		errx(1, "%s:%d - realpath of '%s' result '%s', "	\
76		    "expected '%s", __FILE__, __LINE__, (A), rc.r2,	\
77		    rc.r3);						\
78} while(0);
79
80#define RP_SHOULD_FAIL(A, B, C) do {					\
81	struct rp_compare rc = rpcompare(A, B, C);			\
82	if (rc.r2 != NULL)						\
83		errx(1, "%s:%d - realpath of '%s' should have failed, "	\
84		    "returned '%s'", __FILE__, __LINE__, (A), rc.r2);	\
85	if (rc.r3 != NULL)						\
86		errx(1, "%s:%d - realpath3 of '%s' should have failed, "\
87		    "returned '%s'", __FILE__, __LINE__, (A), rc.r3);	\
88	if (rc.e2 != rc.e3)						\
89		errx(1, "%s:%d - realpath of '%s' errno %d does not "	\
90		    "match realpath3 errno %d", __FILE__, __LINE__, (A),\
91		    rc.e2, rc.e3 );					\
92} while(0);
93
94int
95main(int argc, char *argv[])
96{
97	int i, j;
98	char big[PATH_MAX+PATH_MAX];
99	char r2[PATH_MAX];
100	char r3[PATH_MAX];
101
102	/* some basics */
103	RP_SHOULD_FAIL(NULL, NULL, NULL);
104	RP_SHOULD_FAIL("", NULL, NULL);
105	RP_SHOULD_SUCCEED("/", NULL, NULL);
106	RP_SHOULD_SUCCEED("//", NULL, NULL);
107	RP_SHOULD_SUCCEED("/./", NULL, NULL);
108	RP_SHOULD_SUCCEED("/./.", NULL, NULL);
109	RP_SHOULD_SUCCEED("/./..", NULL, NULL);
110	RP_SHOULD_SUCCEED("/../../", NULL, NULL);
111	RP_SHOULD_SUCCEED("/tmp", NULL, NULL);
112	RP_SHOULD_FAIL("/tmp/noreallydoesntexist", NULL, NULL);
113	RP_SHOULD_FAIL("/tmp/noreallydoesntexist/stillnope", NULL, NULL);
114	RP_SHOULD_SUCCEED("/bin", NULL, NULL);
115	RP_SHOULD_FAIL("/bin/herp", NULL, NULL);
116	RP_SHOULD_SUCCEED("////usr/bin", NULL, NULL);
117	RP_SHOULD_FAIL("//.//usr/bin/.././../herp", r2, r3);
118	RP_SHOULD_SUCCEED("/usr/include/machine/setjmp.h", r2, r3);
119	RP_SHOULD_FAIL("//.//usr/bin/.././../herp/derp", r2, r3);
120	RP_SHOULD_FAIL("/../.../usr/bin", r2, r3);
121	RP_SHOULD_FAIL("/bsd/herp", r2, r3);
122
123	/* relative paths */
124	if (mkdir("hoobla", 0755) == -1) {
125		if (errno != EEXIST)
126			err(1, "mkdir");
127	}
128	RP_SHOULD_SUCCEED("hoobla", r2, r3);
129	RP_SHOULD_FAIL("hoobla/porkrind", r2, r3);
130	RP_SHOULD_FAIL("hoobla/porkrind/peepee", r2, r3);
131
132	/* total size */
133	memset(big, '/', PATH_MAX + 1);
134	RP_SHOULD_FAIL(big, r2, r3);
135
136	/* component size */
137	memset(big, 'a', PATH_MAX + 1);
138	big[0] = '/';
139	big[NAME_MAX+1] = '\0';
140	RP_SHOULD_FAIL(big, r2, r3);
141	memset(big, 'a', PATH_MAX + 1);
142	big[0] = '/';
143	big[NAME_MAX+2] = '\0';
144	RP_SHOULD_FAIL(big, r2, r3);
145
146	/* long relatives back to root */
147	for (i = 0; i < (PATH_MAX - 4); i += 3) {
148		big[i] = '.';
149		big[i+1] = '.';
150		big[i+2] = '/';
151	}
152	i-= 3;
153	strlcpy(big+i, "bsd", 4);
154	RP_SHOULD_SUCCEED(big, r2, r3);
155
156	for (i = 0; i < (PATH_MAX - 5); i += 3) {
157		big[i] = '.';
158		big[i+1] = '.';
159		big[i+2] = '/';
160	}
161	i-= 3;
162	strlcpy(big+i, "bsd/", 5);
163	RP_SHOULD_FAIL(big, r2, r3);
164
165	for (i = 0; i < (PATH_MAX - 5); i += 3) {
166		big[i] = '.';
167		big[i+1] = '.';
168		big[i+2] = '/';
169	}
170	i-= 3;
171	strlcpy(big+i, "derp", 5);
172	RP_SHOULD_FAIL(big, r2, r3);
173
174	for (i = 0; i < (PATH_MAX - 6); i += 3) {
175		big[i] = '.';
176		big[i+1] = '.';
177		big[i+2] = '/';
178	}
179	i-= 3;
180	strlcpy(big+i, "derp/", 6);
181	RP_SHOULD_FAIL(big, r2, r3);
182
183	for (i = 0; i < (PATH_MAX - 4); i += 3) {
184		big[i] = '.';
185		big[i+1] = '.';
186		big[i+2] = '/';
187	}
188	i-= 3;
189	strlcpy(big+i, "xxx", 4);
190	RP_SHOULD_FAIL(big, r2, r3);
191
192	for (i = 0; i < (PATH_MAX - 8); i += 3) {
193		big[i] = '.';
194		big[i+1] = '.';
195		big[i+2] = '/';
196	}
197	i-= 3;
198	strlcpy(big+i, "xxx/../", 8);
199	RP_SHOULD_FAIL(big, r2, r3);
200
201	return (0);
202}
203