1194262Sjhb/*- 2194262Sjhb * Copyright (c) 2009 Advanced Computing Technologies LLC 3194262Sjhb * Written by: John H. Baldwin <jhb@FreeBSD.org> 4194262Sjhb * All rights reserved. 5194262Sjhb * 6194262Sjhb * Redistribution and use in source and binary forms, with or without 7194262Sjhb * modification, are permitted provided that the following conditions 8194262Sjhb * are met: 9194262Sjhb * 1. Redistributions of source code must retain the above copyright 10194262Sjhb * notice, this list of conditions and the following disclaimer. 11194262Sjhb * 2. Redistributions in binary form must reproduce the above copyright 12194262Sjhb * notice, this list of conditions and the following disclaimer in the 13194262Sjhb * documentation and/or other materials provided with the distribution. 14194262Sjhb * 15194262Sjhb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16194262Sjhb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17194262Sjhb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18194262Sjhb * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19194262Sjhb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20194262Sjhb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21194262Sjhb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22194262Sjhb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23194262Sjhb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24194262Sjhb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25194262Sjhb * SUCH DAMAGE. 26194262Sjhb */ 27194262Sjhb 28194262Sjhb#include <sys/cdefs.h> 29194262Sjhb__FBSDID("$FreeBSD$"); 30194262Sjhb 31194262Sjhb/* 32194262Sjhb * Regression tests for the closefrom(2) system call. 33194262Sjhb */ 34194262Sjhb 35194262Sjhb#include <sys/param.h> 36194262Sjhb#include <sys/mman.h> 37194262Sjhb#include <sys/user.h> 38194262Sjhb#include <sys/wait.h> 39194262Sjhb#include <errno.h> 40194262Sjhb#include <fcntl.h> 41194262Sjhb#include <libutil.h> 42194262Sjhb#include <stdarg.h> 43194262Sjhb#include <stdio.h> 44194262Sjhb#include <stdlib.h> 45194262Sjhb#include <string.h> 46194262Sjhb#include <unistd.h> 47194262Sjhb 48194262Sjhbstruct shared_info { 49194262Sjhb int failed; 50194262Sjhb char tag[64]; 51194262Sjhb char message[0]; 52194262Sjhb}; 53194262Sjhb 54194262Sjhbstatic int test = 1; 55194262Sjhb 56194262Sjhbstatic void 57194262Sjhbok(const char *descr) 58194262Sjhb{ 59194262Sjhb 60194262Sjhb printf("ok %d - %s\n", test, descr); 61194262Sjhb test++; 62194262Sjhb} 63194262Sjhb 64194262Sjhbstatic void 65194262Sjhbfail(const char *descr, const char *fmt, ...) 66194262Sjhb{ 67194262Sjhb va_list ap; 68194262Sjhb 69194262Sjhb printf("not ok %d - %s", test, descr); 70194262Sjhb test++; 71194262Sjhb if (fmt) { 72194262Sjhb va_start(ap, fmt); 73194262Sjhb printf(" # "); 74194262Sjhb vprintf(fmt, ap); 75194262Sjhb va_end(ap); 76194262Sjhb } 77194262Sjhb printf("\n"); 78194262Sjhb exit(1); 79194262Sjhb} 80194262Sjhb 81194262Sjhb#define fail_err(descr) fail((descr), "%s", strerror(errno)) 82194262Sjhb 83194262Sjhbstatic void 84194262Sjhbcok(struct shared_info *info, const char *descr) 85194262Sjhb{ 86194262Sjhb 87194262Sjhb info->failed = 0; 88194262Sjhb strlcpy(info->tag, descr, sizeof(info->tag)); 89194262Sjhb exit(0); 90194262Sjhb} 91194262Sjhb 92194262Sjhbstatic void 93194262Sjhbcfail(struct shared_info *info, const char *descr, const char *fmt, ...) 94194262Sjhb{ 95194262Sjhb va_list ap; 96194262Sjhb 97194262Sjhb info->failed = 1; 98194262Sjhb strlcpy(info->tag, descr, sizeof(info->tag)); 99194262Sjhb if (fmt) { 100194262Sjhb va_start(ap, fmt); 101194262Sjhb vsprintf(info->message, fmt, ap); 102194262Sjhb va_end(ap); 103194262Sjhb } 104194262Sjhb exit(0); 105194262Sjhb} 106194262Sjhb 107194262Sjhb#define cfail_err(info, descr) cfail((info), (descr), "%s", strerror(errno)) 108194262Sjhb 109194262Sjhb/* 110194262Sjhb * Use kinfo_getfile() to fetch the list of file descriptors and figure out 111194262Sjhb * the highest open file descriptor. 112194262Sjhb */ 113194262Sjhbstatic int 114194262Sjhbhighest_fd(void) 115194262Sjhb{ 116194262Sjhb struct kinfo_file *kif; 117194262Sjhb int cnt, i, highest; 118194262Sjhb 119194262Sjhb kif = kinfo_getfile(getpid(), &cnt); 120194262Sjhb if (kif == NULL) 121194262Sjhb fail_err("kinfo_getfile"); 122194262Sjhb highest = INT_MIN; 123194262Sjhb for (i = 0; i < cnt; i++) 124194262Sjhb if (kif[i].kf_fd > highest) 125194262Sjhb highest = kif[i].kf_fd; 126194262Sjhb free(kif); 127194262Sjhb return (highest); 128194262Sjhb} 129194262Sjhb 130194262Sjhbstatic int 131194262Sjhbdevnull(void) 132194262Sjhb{ 133194262Sjhb int fd; 134194262Sjhb 135194262Sjhb fd = open("/dev/null", O_RDONLY); 136194262Sjhb if (fd < 0) 137194262Sjhb fail_err("open(\"/dev/null\")"); 138194262Sjhb return (fd); 139194262Sjhb} 140194262Sjhb 141194262Sjhbint 142194262Sjhbmain(int __unused argc, char __unused *argv[]) 143194262Sjhb{ 144194262Sjhb struct shared_info *info; 145194262Sjhb pid_t pid; 146194262Sjhb int fd, i; 147194262Sjhb 148194262Sjhb printf("1..15\n"); 149194262Sjhb 150194262Sjhb /* We better start up with fd's 0, 1, and 2 open. */ 151194262Sjhb fd = devnull(); 152194262Sjhb if (fd != 3) 153194262Sjhb fail("open", "bad descriptor %d", fd); 154194262Sjhb ok("open"); 155194262Sjhb 156194262Sjhb /* Make sure highest_fd() works. */ 157194262Sjhb fd = highest_fd(); 158194262Sjhb if (fd != 3) 159194262Sjhb fail("highest_fd", "bad descriptor %d", fd); 160194262Sjhb ok("highest_fd"); 161194262Sjhb 162194262Sjhb /* Try to use closefrom() for just closing fd 3. */ 163194262Sjhb closefrom(3); 164194262Sjhb fd = highest_fd(); 165194262Sjhb if (fd != 2) 166194262Sjhb fail("closefrom", "highest fd %d", fd); 167194262Sjhb ok("closefrom"); 168194262Sjhb 169194262Sjhb /* Eat up 16 descriptors. */ 170194262Sjhb for (i = 0; i < 16; i++) 171194262Sjhb (void)devnull(); 172194262Sjhb fd = highest_fd(); 173194262Sjhb if (fd != 18) 174194262Sjhb fail("open 16", "highest fd %d", fd); 175194262Sjhb ok("open 16"); 176194262Sjhb 177194262Sjhb /* Close half of them. */ 178194262Sjhb closefrom(11); 179194262Sjhb fd = highest_fd(); 180194262Sjhb if (fd != 10) 181194262Sjhb fail("closefrom", "highest fd %d", fd); 182194262Sjhb ok("closefrom"); 183194262Sjhb 184194262Sjhb /* Explicitly close descriptors 6 and 8 to create holes. */ 185194262Sjhb if (close(6) < 0 || close(8) < 0) 186194262Sjhb fail_err("close2 "); 187194262Sjhb ok("close 2"); 188194262Sjhb 189194262Sjhb /* Verify that close on 6 and 8 fails with EBADF. */ 190194262Sjhb if (close(6) == 0) 191194262Sjhb fail("close(6)", "did not fail"); 192194262Sjhb if (errno != EBADF) 193194262Sjhb fail_err("close(6)"); 194194262Sjhb ok("close(6)"); 195194262Sjhb if (close(8) == 0) 196194262Sjhb fail("close(8)", "did not fail"); 197194262Sjhb if (errno != EBADF) 198194262Sjhb fail_err("close(8)"); 199194262Sjhb ok("close(8)"); 200194262Sjhb 201194262Sjhb /* Close from 4 on. */ 202194262Sjhb closefrom(4); 203194262Sjhb fd = highest_fd(); 204194262Sjhb if (fd != 3) 205194262Sjhb fail("closefrom", "highest fd %d", fd); 206194262Sjhb ok("closefrom"); 207194262Sjhb 208194262Sjhb /* Allocate a small SHM region for IPC with our child. */ 209194262Sjhb info = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_ANON | 210194262Sjhb MAP_SHARED, -1, 0); 211194262Sjhb if (info == MAP_FAILED) 212194262Sjhb fail_err("mmap"); 213194262Sjhb ok("mmap"); 214194262Sjhb 215194262Sjhb /* Fork a child process to test closefrom(0). */ 216194262Sjhb pid = fork(); 217194262Sjhb if (pid < 0) 218194262Sjhb fail_err("fork"); 219194262Sjhb if (pid == 0) { 220194262Sjhb /* Child. */ 221194262Sjhb closefrom(0); 222194262Sjhb fd = highest_fd(); 223194262Sjhb if (fd >= 0) 224194262Sjhb cfail(info, "closefrom(0)", "highest fd %d", fd); 225194262Sjhb cok(info, "closefrom(0)"); 226194262Sjhb } 227194262Sjhb if (wait(NULL) < 0) 228194262Sjhb fail_err("wait"); 229194262Sjhb if (info->failed) 230194262Sjhb fail(info->tag, "%s", info->message); 231194262Sjhb ok(info->tag); 232194262Sjhb 233194262Sjhb /* Fork a child process to test closefrom(-1). */ 234194262Sjhb pid = fork(); 235194262Sjhb if (pid < 0) 236194262Sjhb fail_err("fork"); 237194262Sjhb if (pid == 0) { 238194262Sjhb /* Child. */ 239194262Sjhb closefrom(-1); 240194262Sjhb fd = highest_fd(); 241194262Sjhb if (fd >= 0) 242194262Sjhb cfail(info, "closefrom(-1)", "highest fd %d", fd); 243194262Sjhb cok(info, "closefrom(-1)"); 244194262Sjhb } 245194262Sjhb if (wait(NULL) < 0) 246194262Sjhb fail_err("wait"); 247194262Sjhb if (info->failed) 248194262Sjhb fail(info->tag, "%s", info->message); 249194262Sjhb ok(info->tag); 250194262Sjhb 251194262Sjhb /* Dup stdout to 6. */ 252194262Sjhb if (dup2(1, 6) < 0) 253194262Sjhb fail_err("dup2"); 254194262Sjhb fd = highest_fd(); 255194262Sjhb if (fd != 6) 256194262Sjhb fail("dup2", "highest fd %d", fd); 257194262Sjhb ok("dup2"); 258194262Sjhb 259194262Sjhb /* Do a closefrom() starting in a hole. */ 260194262Sjhb closefrom(4); 261194262Sjhb fd = highest_fd(); 262194262Sjhb if (fd != 3) 263194262Sjhb fail("closefrom", "highest fd %d", fd); 264194262Sjhb ok("closefrom"); 265194262Sjhb 266194262Sjhb /* Do a closefrom() beyond our highest open fd. */ 267194262Sjhb closefrom(32); 268194262Sjhb fd = highest_fd(); 269194262Sjhb if (fd != 3) 270194262Sjhb fail("closefrom", "highest fd %d", fd); 271194262Sjhb ok("closefrom"); 272194262Sjhb 273194262Sjhb return (0); 274194262Sjhb} 275