1 | /* |
2 | * Copyright © 2018 Alexey Dobriyan <adobriyan@gmail.com> |
3 | * |
4 | * Permission to use, copy, modify, and distribute this software for any |
5 | * purpose with or without fee is hereby granted, provided that the above |
6 | * copyright notice and this permission notice appear in all copies. |
7 | * |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | */ |
16 | // Test /proc/*/fd lookup. |
17 | |
18 | #undef NDEBUG |
19 | #include <assert.h> |
20 | #include <dirent.h> |
21 | #include <errno.h> |
22 | #include <limits.h> |
23 | #include <sched.h> |
24 | #include <stdio.h> |
25 | #include <unistd.h> |
26 | #include <sys/types.h> |
27 | #include <sys/stat.h> |
28 | #include <fcntl.h> |
29 | |
30 | #include "proc.h" |
31 | |
32 | /* lstat(2) has more "coverage" in case non-symlink pops up somehow. */ |
33 | static void test_lookup_pass(const char *pathname) |
34 | { |
35 | struct stat st; |
36 | ssize_t rv; |
37 | |
38 | memset(&st, 0, sizeof(struct stat)); |
39 | rv = lstat(pathname, &st); |
40 | assert(rv == 0); |
41 | assert(S_ISLNK(st.st_mode)); |
42 | } |
43 | |
44 | static void test_lookup_fail(const char *pathname) |
45 | { |
46 | struct stat st; |
47 | ssize_t rv; |
48 | |
49 | rv = lstat(pathname, &st); |
50 | assert(rv == -1 && errno == ENOENT); |
51 | } |
52 | |
53 | static void test_lookup(unsigned int fd) |
54 | { |
55 | char buf[64]; |
56 | unsigned int c; |
57 | unsigned int u; |
58 | int i; |
59 | |
60 | snprintf(buf, sizeof(buf), "/proc/self/fd/%u" , fd); |
61 | test_lookup_pass(pathname: buf); |
62 | |
63 | /* leading junk */ |
64 | for (c = 1; c <= 255; c++) { |
65 | if (c == '/') |
66 | continue; |
67 | snprintf(buf, sizeof(buf), "/proc/self/fd/%c%u" , c, fd); |
68 | test_lookup_fail(pathname: buf); |
69 | } |
70 | |
71 | /* trailing junk */ |
72 | for (c = 1; c <= 255; c++) { |
73 | if (c == '/') |
74 | continue; |
75 | snprintf(buf, sizeof(buf), "/proc/self/fd/%u%c" , fd, c); |
76 | test_lookup_fail(pathname: buf); |
77 | } |
78 | |
79 | for (i = INT_MIN; i < INT_MIN + 1024; i++) { |
80 | snprintf(buf, sizeof(buf), "/proc/self/fd/%d" , i); |
81 | test_lookup_fail(pathname: buf); |
82 | } |
83 | for (i = -1024; i < 0; i++) { |
84 | snprintf(buf, sizeof(buf), "/proc/self/fd/%d" , i); |
85 | test_lookup_fail(pathname: buf); |
86 | } |
87 | for (u = INT_MAX - 1024; u <= (unsigned int)INT_MAX + 1024; u++) { |
88 | snprintf(buf, sizeof(buf), "/proc/self/fd/%u" , u); |
89 | test_lookup_fail(buf); |
90 | } |
91 | for (u = UINT_MAX - 1024; u != 0; u++) { |
92 | snprintf(buf, sizeof(buf), "/proc/self/fd/%u" , u); |
93 | test_lookup_fail(pathname: buf); |
94 | } |
95 | |
96 | |
97 | } |
98 | |
99 | int main(void) |
100 | { |
101 | struct dirent *de; |
102 | unsigned int fd, target_fd; |
103 | |
104 | if (unshare(CLONE_FILES) == -1) |
105 | return 1; |
106 | |
107 | /* Wipe fdtable. */ |
108 | do { |
109 | DIR *d; |
110 | |
111 | d = opendir("/proc/self/fd" ); |
112 | if (!d) |
113 | return 1; |
114 | |
115 | de = xreaddir(d); |
116 | assert(de->d_type == DT_DIR); |
117 | assert(streq(de->d_name, "." )); |
118 | |
119 | de = xreaddir(d); |
120 | assert(de->d_type == DT_DIR); |
121 | assert(streq(de->d_name, ".." )); |
122 | next: |
123 | de = xreaddir(d); |
124 | if (de) { |
125 | unsigned long long fd_ull; |
126 | unsigned int fd; |
127 | char *end; |
128 | |
129 | assert(de->d_type == DT_LNK); |
130 | |
131 | fd_ull = xstrtoull(p: de->d_name, end: &end); |
132 | assert(*end == '\0'); |
133 | assert(fd_ull == (unsigned int)fd_ull); |
134 | |
135 | fd = fd_ull; |
136 | if (fd == dirfd(d)) |
137 | goto next; |
138 | close(fd); |
139 | } |
140 | |
141 | closedir(d); |
142 | } while (de); |
143 | |
144 | /* Now fdtable is clean. */ |
145 | |
146 | fd = open("/" , O_PATH|O_DIRECTORY); |
147 | assert(fd == 0); |
148 | test_lookup(fd); |
149 | close(fd); |
150 | |
151 | /* Clean again! */ |
152 | |
153 | fd = open("/" , O_PATH|O_DIRECTORY); |
154 | assert(fd == 0); |
155 | /* Default RLIMIT_NOFILE-1 */ |
156 | target_fd = 1023; |
157 | while (target_fd > 0) { |
158 | if (dup2(fd, target_fd) == target_fd) |
159 | break; |
160 | target_fd /= 2; |
161 | } |
162 | assert(target_fd > 0); |
163 | close(fd); |
164 | test_lookup(fd: target_fd); |
165 | close(target_fd); |
166 | |
167 | return 0; |
168 | } |
169 | |