• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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