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