|  | /* | 
|  | * Copyright © 2018 Alexey Dobriyan <adobriyan@gmail.com> | 
|  | * | 
|  | * Permission to use, copy, modify, and distribute this software for any | 
|  | * purpose with or without fee is hereby granted, provided that the above | 
|  | * copyright notice and this permission notice appear in all copies. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | 
|  | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | 
|  | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | 
|  | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | 
|  | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | 
|  | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | 
|  | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
|  | */ | 
|  | // Test /proc/*/fd lookup. | 
|  |  | 
|  | #undef NDEBUG | 
|  | #include <assert.h> | 
|  | #include <dirent.h> | 
|  | #include <errno.h> | 
|  | #include <limits.h> | 
|  | #include <sched.h> | 
|  | #include <stdio.h> | 
|  | #include <unistd.h> | 
|  | #include <sys/types.h> | 
|  | #include <sys/stat.h> | 
|  | #include <fcntl.h> | 
|  |  | 
|  | #include "proc.h" | 
|  |  | 
|  | /* lstat(2) has more "coverage" in case non-symlink pops up somehow. */ | 
|  | static void test_lookup_pass(const char *pathname) | 
|  | { | 
|  | struct stat st; | 
|  | ssize_t rv; | 
|  |  | 
|  | memset(&st, 0, sizeof(struct stat)); | 
|  | rv = lstat(pathname, &st); | 
|  | assert(rv == 0); | 
|  | assert(S_ISLNK(st.st_mode)); | 
|  | } | 
|  |  | 
|  | static void test_lookup_fail(const char *pathname) | 
|  | { | 
|  | struct stat st; | 
|  | ssize_t rv; | 
|  |  | 
|  | rv = lstat(pathname, &st); | 
|  | assert(rv == -1 && errno == ENOENT); | 
|  | } | 
|  |  | 
|  | static void test_lookup(unsigned int fd) | 
|  | { | 
|  | char buf[64]; | 
|  | unsigned int c; | 
|  | unsigned int u; | 
|  | int i; | 
|  |  | 
|  | snprintf(buf, sizeof(buf), "/proc/self/fd/%u", fd); | 
|  | test_lookup_pass(buf); | 
|  |  | 
|  | /* leading junk */ | 
|  | for (c = 1; c <= 255; c++) { | 
|  | if (c == '/') | 
|  | continue; | 
|  | snprintf(buf, sizeof(buf), "/proc/self/fd/%c%u", c, fd); | 
|  | test_lookup_fail(buf); | 
|  | } | 
|  |  | 
|  | /* trailing junk */ | 
|  | for (c = 1; c <= 255; c++) { | 
|  | if (c == '/') | 
|  | continue; | 
|  | snprintf(buf, sizeof(buf), "/proc/self/fd/%u%c", fd, c); | 
|  | test_lookup_fail(buf); | 
|  | } | 
|  |  | 
|  | for (i = INT_MIN; i < INT_MIN + 1024; i++) { | 
|  | snprintf(buf, sizeof(buf), "/proc/self/fd/%d", i); | 
|  | test_lookup_fail(buf); | 
|  | } | 
|  | for (i = -1024; i < 0; i++) { | 
|  | snprintf(buf, sizeof(buf), "/proc/self/fd/%d", i); | 
|  | test_lookup_fail(buf); | 
|  | } | 
|  | for (u = INT_MAX - 1024; u <= (unsigned int)INT_MAX + 1024; u++) { | 
|  | snprintf(buf, sizeof(buf), "/proc/self/fd/%u", u); | 
|  | test_lookup_fail(buf); | 
|  | } | 
|  | for (u = UINT_MAX - 1024; u != 0; u++) { | 
|  | snprintf(buf, sizeof(buf), "/proc/self/fd/%u", u); | 
|  | test_lookup_fail(buf); | 
|  | } | 
|  |  | 
|  |  | 
|  | } | 
|  |  | 
|  | int main(void) | 
|  | { | 
|  | struct dirent *de; | 
|  | unsigned int fd, target_fd; | 
|  |  | 
|  | if (unshare(CLONE_FILES) == -1) | 
|  | return 1; | 
|  |  | 
|  | /* Wipe fdtable. */ | 
|  | do { | 
|  | DIR *d; | 
|  |  | 
|  | d = opendir("/proc/self/fd"); | 
|  | if (!d) | 
|  | return 1; | 
|  |  | 
|  | de = xreaddir(d); | 
|  | assert(de->d_type == DT_DIR); | 
|  | assert(streq(de->d_name, ".")); | 
|  |  | 
|  | de = xreaddir(d); | 
|  | assert(de->d_type == DT_DIR); | 
|  | assert(streq(de->d_name, "..")); | 
|  | next: | 
|  | de = xreaddir(d); | 
|  | if (de) { | 
|  | unsigned long long fd_ull; | 
|  | unsigned int fd; | 
|  | char *end; | 
|  |  | 
|  | assert(de->d_type == DT_LNK); | 
|  |  | 
|  | fd_ull = xstrtoull(de->d_name, &end); | 
|  | assert(*end == '\0'); | 
|  | assert(fd_ull == (unsigned int)fd_ull); | 
|  |  | 
|  | fd = fd_ull; | 
|  | if (fd == dirfd(d)) | 
|  | goto next; | 
|  | close(fd); | 
|  | } | 
|  |  | 
|  | closedir(d); | 
|  | } while (de); | 
|  |  | 
|  | /* Now fdtable is clean. */ | 
|  |  | 
|  | fd = open("/", O_PATH|O_DIRECTORY); | 
|  | assert(fd == 0); | 
|  | test_lookup(fd); | 
|  | close(fd); | 
|  |  | 
|  | /* Clean again! */ | 
|  |  | 
|  | fd = open("/", O_PATH|O_DIRECTORY); | 
|  | assert(fd == 0); | 
|  | /* Default RLIMIT_NOFILE-1 */ | 
|  | target_fd = 1023; | 
|  | while (target_fd > 0) { | 
|  | if (dup2(fd, target_fd) == target_fd) | 
|  | break; | 
|  | target_fd /= 2; | 
|  | } | 
|  | assert(target_fd > 0); | 
|  | close(fd); | 
|  | test_lookup(target_fd); | 
|  | close(target_fd); | 
|  |  | 
|  | return 0; | 
|  | } |