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 29 FILE * 30 os_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 40 int 41 os_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 50 int 51 os_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 86 typedef ptrdiff_t ssize_t; 87 #endif 88 89 static ssize_t 90 readN(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 124 char * 125 os_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 202 int 203 os_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 216 int 217 os_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