1/* 2 * Copyright 2019 Intel Corporation 3 * SPDX-License-Identifier: MIT 4 */ 5 6#include "os_file.h" 7#include "detect_os.h" 8 9#include <errno.h> 10#include <fcntl.h> 11#include <stdlib.h> 12#include <sys/stat.h> 13 14#if DETECT_OS_WINDOWS 15#include <io.h> 16#define open _open 17#define fdopen _fdopen 18#define O_CREAT _O_CREAT 19#define O_EXCL _O_EXCL 20#define O_WRONLY _O_WRONLY 21#else 22#include <unistd.h> 23#ifndef F_DUPFD_CLOEXEC 24#define F_DUPFD_CLOEXEC 1030 25#endif 26#endif 27 28 29FILE * 30os_file_create_unique(const char *filename, int filemode) 31{ 32 int fd = open(filename, O_CREAT | O_EXCL | O_WRONLY, filemode); 33 if (fd == -1) 34 return NULL; 35 return fdopen(fd, "w"); 36} 37 38 39#if DETECT_OS_WINDOWS 40int 41os_dupfd_cloexec(int fd) 42{ 43 /* 44 * On Windows child processes don't inherit handles by default: 45 * https://devblogs.microsoft.com/oldnewthing/20111216-00/?p=8873 46 */ 47 return dup(fd); 48} 49#else 50int 51os_dupfd_cloexec(int fd) 52{ 53 int minfd = 3; 54 int newfd = fcntl(fd, F_DUPFD_CLOEXEC, minfd); 55 56 if (newfd >= 0) 57 return newfd; 58 59 if (errno != EINVAL) 60 return -1; 61 62 newfd = fcntl(fd, F_DUPFD, minfd); 63 64 if (newfd < 0) 65 return -1; 66 67 long flags = fcntl(newfd, F_GETFD); 68 if (flags == -1) { 69 close(newfd); 70 return -1; 71 } 72 73 if (fcntl(newfd, F_SETFD, flags | FD_CLOEXEC) == -1) { 74 close(newfd); 75 return -1; 76 } 77 78 return newfd; 79} 80#endif 81 82#include <fcntl.h> 83#include <sys/stat.h> 84 85#if DETECT_OS_WINDOWS 86typedef ptrdiff_t ssize_t; 87#endif 88 89static ssize_t 90readN(int fd, char *buf, size_t len) 91{ 92 /* err was initially set to -ENODATA but in some BSD systems 93 * ENODATA is not defined and ENOATTR is used instead. 94 * As err is not returned by any function it can be initialized 95 * to -EFAULT that exists everywhere. 96 */ 97 int err = -EFAULT; 98 size_t total = 0; 99 do { 100 ssize_t ret = read(fd, buf + total, len - total); 101 102 if (ret < 0) 103 ret = -errno; 104 105 if (ret == -EINTR || ret == -EAGAIN) 106 continue; 107 108 if (ret <= 0) { 109 err = ret; 110 break; 111 } 112 113 total += ret; 114 } while (total != len); 115 116 return total ? (ssize_t)total : err; 117} 118 119#ifndef O_BINARY 120/* Unix makes no distinction between text and binary files. */ 121#define O_BINARY 0 122#endif 123 124char * 125os_read_file(const char *filename, size_t *size) 126{ 127 /* Note that this also serves as a slight margin to avoid a 2x grow when 128 * the file is just a few bytes larger when we read it than when we 129 * fstat'ed it. 130 * The string's NULL terminator is also included in here. 131 */ 132 size_t len = 64; 133 134 int fd = open(filename, O_RDONLY | O_BINARY); 135 if (fd == -1) { 136 /* errno set by open() */ 137 return NULL; 138 } 139 140 /* Pre-allocate a buffer at least the size of the file if we can read 141 * that information. 142 */ 143 struct stat stat; 144 if (fstat(fd, &stat) == 0) 145 len += stat.st_size; 146 147 char *buf = malloc(len); 148 if (!buf) { 149 close(fd); 150 errno = -ENOMEM; 151 return NULL; 152 } 153 154 ssize_t actually_read; 155 size_t offset = 0, remaining = len - 1; 156 while ((actually_read = readN(fd, buf + offset, remaining)) == (ssize_t)remaining) { 157 char *newbuf = realloc(buf, 2 * len); 158 if (!newbuf) { 159 free(buf); 160 close(fd); 161 errno = -ENOMEM; 162 return NULL; 163 } 164 165 buf = newbuf; 166 len *= 2; 167 offset += actually_read; 168 remaining = len - offset - 1; 169 } 170 171 close(fd); 172 173 if (actually_read > 0) 174 offset += actually_read; 175 176 /* Final resize to actual size */ 177 len = offset + 1; 178 char *newbuf = realloc(buf, len); 179 if (!newbuf) { 180 free(buf); 181 errno = -ENOMEM; 182 return NULL; 183 } 184 buf = newbuf; 185 186 buf[offset] = '\0'; 187 188 if (size) 189 *size = offset; 190 191 return buf; 192} 193 194#if DETECT_OS_LINUX 195 196#include <sys/syscall.h> 197#include <unistd.h> 198 199/* copied from <linux/kcmp.h> */ 200#define KCMP_FILE 0 201 202int 203os_same_file_description(int fd1, int fd2) 204{ 205 pid_t pid = getpid(); 206 207 /* Same file descriptor trivially implies same file description */ 208 if (fd1 == fd2) 209 return 0; 210 211 return syscall(SYS_kcmp, pid, pid, KCMP_FILE, fd1, fd2); 212} 213 214#else 215 216int 217os_same_file_description(int fd1, int fd2) 218{ 219 /* Same file descriptor trivially implies same file description */ 220 if (fd1 == fd2) 221 return 0; 222 223 /* Otherwise we can't tell */ 224 return -1; 225} 226 227#endif 228