1 /* $OpenBSD: realpathtest.c,v 1.7 2019/05/29 13:53:59 beck 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 */ 34 extern char *realpath3(const char *pathname, char *resolved); 35 36 struct rp_compare { 37 char * resolv2; 38 char * resolv3; 39 char * r2; 40 char * r3; 41 int e2; 42 int e3; 43 }; 44 45 static struct rp_compare 46 rpcompare(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 94 int 95 main(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_SUCCEED("/bin/c++", NULL, NULL); 104 RP_SHOULD_SUCCEED("/tmp", NULL, NULL); 105 RP_SHOULD_SUCCEED("/tmp/noreallydoesntexist", NULL, NULL); 106 RP_SHOULD_FAIL("/tmp/noreallydoesntexist/stillnope", NULL, NULL); 107 RP_SHOULD_SUCCEED("/bin", NULL, NULL); 108 RP_SHOULD_SUCCEED("/bin/herp", NULL, NULL); 109 RP_SHOULD_SUCCEED("////usr/bin", NULL, NULL); 110 RP_SHOULD_SUCCEED("//.//usr/bin/.././../herp", r2, r3); 111 RP_SHOULD_SUCCEED("/usr/include/machine/setjmp.h", r2, r3); 112 RP_SHOULD_FAIL("//.//usr/bin/.././../herp/derp", r2, r3); 113 RP_SHOULD_FAIL("/../.../usr/bin", r2, r3); 114 RP_SHOULD_FAIL("/bsd/herp", r2, r3); 115 116 /* relative paths */ 117 if (mkdir("hoobla", 0755) == -1) { 118 if (errno != EEXIST) 119 err(1, "mkdir"); 120 } 121 RP_SHOULD_SUCCEED("hoobla", r2, r3); 122 RP_SHOULD_SUCCEED("hoobla/porkrind", r2, r3); /* XXX posix */ 123 RP_SHOULD_FAIL("hoobla/porkrind/peepee", r2, r3); 124 125 /* total size */ 126 memset(big, '/', PATH_MAX + 1); 127 RP_SHOULD_FAIL(big, r2, r3); 128 129 /* component size */ 130 memset(big, 'a', PATH_MAX + 1); 131 big[0] = '/'; 132 big[NAME_MAX+1] = '\0'; 133 RP_SHOULD_SUCCEED(big, r2, r3); 134 memset(big, 'a', PATH_MAX + 1); 135 big[0] = '/'; 136 big[NAME_MAX+2] = '\0'; 137 RP_SHOULD_FAIL(big, r2, r3); 138 139 /* long relatives back to root */ 140 for (i = 0; i < (PATH_MAX - 4); i += 3) { 141 big[i] = '.'; 142 big[i+1] = '.'; 143 big[i+2] = '/'; 144 } 145 i-= 3; 146 strlcpy(big+i, "bsd", 4); 147 RP_SHOULD_SUCCEED(big, r2, r3); 148 149 for (i = 0; i < (PATH_MAX - 5); i += 3) { 150 big[i] = '.'; 151 big[i+1] = '.'; 152 big[i+2] = '/'; 153 } 154 i-= 3; 155 strlcpy(big+i, "bsd/", 5); 156 RP_SHOULD_SUCCEED(big, r2, r3); /* XXX This is wrong by posix */ 157 158 for (i = 0; i < (PATH_MAX - 5); i += 3) { 159 big[i] = '.'; 160 big[i+1] = '.'; 161 big[i+2] = '/'; 162 } 163 i-= 3; 164 strlcpy(big+i, "derp", 5); 165 RP_SHOULD_SUCCEED(big, r2, r3); /* XXX this is wrong by posix */ 166 167 for (i = 0; i < (PATH_MAX - 6); i += 3) { 168 big[i] = '.'; 169 big[i+1] = '.'; 170 big[i+2] = '/'; 171 } 172 i-= 3; 173 strlcpy(big+i, "derp/", 6); 174 RP_SHOULD_FAIL(big, r2, r3); /* XXX this is correct by posix */ 175 176 for (i = 0; i < (PATH_MAX - 4); i += 3) { 177 big[i] = '.'; 178 big[i+1] = '.'; 179 big[i+2] = '/'; 180 } 181 i-= 3; 182 strlcpy(big+i, "xxx", 4); 183 RP_SHOULD_SUCCEED(big, r2, r3); 184 185 for (i = 0; i < (PATH_MAX - 8); i += 3) { 186 big[i] = '.'; 187 big[i+1] = '.'; 188 big[i+2] = '/'; 189 } 190 i-= 3; 191 strlcpy(big+i, "xxx/../", 8); 192 RP_SHOULD_FAIL(big, r2, r3); 193 194 return (0); 195 } 196