/src/libgit2/src/util/posix.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) the libgit2 contributors. All rights reserved. |
3 | | * |
4 | | * This file is part of libgit2, distributed under the GNU GPL v2 with |
5 | | * a Linking Exception. For full terms see the included COPYING file. |
6 | | */ |
7 | | |
8 | | #include "posix.h" |
9 | | |
10 | | #include "fs_path.h" |
11 | | #include <stdio.h> |
12 | | #include <ctype.h> |
13 | | |
14 | | size_t p_fsync__cnt = 0; |
15 | | |
16 | | #ifndef GIT_WIN32 |
17 | | |
18 | | #ifdef NO_ADDRINFO |
19 | | |
20 | | int p_getaddrinfo( |
21 | | const char *host, |
22 | | const char *port, |
23 | | struct addrinfo *hints, |
24 | | struct addrinfo **info) |
25 | | { |
26 | | struct addrinfo *ainfo, *ai; |
27 | | int p = 0; |
28 | | |
29 | | GIT_UNUSED(hints); |
30 | | |
31 | | if ((ainfo = git__malloc(sizeof(struct addrinfo))) == NULL) |
32 | | return -1; |
33 | | |
34 | | if ((ainfo->ai_hostent = gethostbyname(host)) == NULL) { |
35 | | git__free(ainfo); |
36 | | return -2; |
37 | | } |
38 | | |
39 | | ainfo->ai_servent = getservbyname(port, 0); |
40 | | |
41 | | if (ainfo->ai_servent) |
42 | | ainfo->ai_port = ainfo->ai_servent->s_port; |
43 | | else |
44 | | ainfo->ai_port = htons(atol(port)); |
45 | | |
46 | | memcpy(&ainfo->ai_addr_in.sin_addr, |
47 | | ainfo->ai_hostent->h_addr_list[0], |
48 | | ainfo->ai_hostent->h_length); |
49 | | |
50 | | ainfo->ai_protocol = 0; |
51 | | ainfo->ai_socktype = hints->ai_socktype; |
52 | | ainfo->ai_family = ainfo->ai_hostent->h_addrtype; |
53 | | ainfo->ai_addr_in.sin_family = ainfo->ai_family; |
54 | | ainfo->ai_addr_in.sin_port = ainfo->ai_port; |
55 | | ainfo->ai_addr = (struct addrinfo *)&ainfo->ai_addr_in; |
56 | | ainfo->ai_addrlen = sizeof(struct sockaddr_in); |
57 | | |
58 | | *info = ainfo; |
59 | | |
60 | | if (ainfo->ai_hostent->h_addr_list[1] == NULL) { |
61 | | ainfo->ai_next = NULL; |
62 | | return 0; |
63 | | } |
64 | | |
65 | | ai = ainfo; |
66 | | |
67 | | for (p = 1; ainfo->ai_hostent->h_addr_list[p] != NULL; p++) { |
68 | | if (!(ai->ai_next = git__malloc(sizeof(struct addrinfo)))) { |
69 | | p_freeaddrinfo(ainfo); |
70 | | return -1; |
71 | | } |
72 | | memcpy(ai->ai_next, ainfo, sizeof(struct addrinfo)); |
73 | | memcpy(&ai->ai_next->ai_addr_in.sin_addr, |
74 | | ainfo->ai_hostent->h_addr_list[p], |
75 | | ainfo->ai_hostent->h_length); |
76 | | ai->ai_next->ai_addr = (struct addrinfo *)&ai->ai_next->ai_addr_in; |
77 | | ai = ai->ai_next; |
78 | | } |
79 | | |
80 | | ai->ai_next = NULL; |
81 | | return 0; |
82 | | } |
83 | | |
84 | | void p_freeaddrinfo(struct addrinfo *info) |
85 | | { |
86 | | struct addrinfo *p, *next; |
87 | | |
88 | | p = info; |
89 | | |
90 | | while(p != NULL) { |
91 | | next = p->ai_next; |
92 | | git__free(p); |
93 | | p = next; |
94 | | } |
95 | | } |
96 | | |
97 | | const char *p_gai_strerror(int ret) |
98 | | { |
99 | | switch(ret) { |
100 | | case -1: return "Out of memory"; break; |
101 | | case -2: return "Address lookup failed"; break; |
102 | | default: return "Unknown error"; break; |
103 | | } |
104 | | } |
105 | | |
106 | | #endif /* NO_ADDRINFO */ |
107 | | |
108 | | int p_open(const char *path, volatile int flags, ...) |
109 | 7.75k | { |
110 | 7.75k | mode_t mode = 0; |
111 | | |
112 | | #ifdef GIT_DEBUG_STRICT_OPEN |
113 | | if (strstr(path, "//") != NULL) { |
114 | | errno = EACCES; |
115 | | return -1; |
116 | | } |
117 | | #endif |
118 | | |
119 | 7.75k | if (flags & O_CREAT) { |
120 | 19 | va_list arg_list; |
121 | | |
122 | 19 | va_start(arg_list, flags); |
123 | 19 | mode = (mode_t)va_arg(arg_list, int); |
124 | 19 | va_end(arg_list); |
125 | 19 | } |
126 | | |
127 | 7.75k | return open(path, flags | O_BINARY | O_CLOEXEC, mode); |
128 | 7.75k | } |
129 | | |
130 | | int p_creat(const char *path, mode_t mode) |
131 | 2 | { |
132 | 2 | return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC, mode); |
133 | 2 | } |
134 | | |
135 | | int p_getcwd(char *buffer_out, size_t size) |
136 | 0 | { |
137 | 0 | char *cwd_buffer; |
138 | |
|
139 | 0 | GIT_ASSERT_ARG(buffer_out); |
140 | 0 | GIT_ASSERT_ARG(size > 0); |
141 | | |
142 | 0 | cwd_buffer = getcwd(buffer_out, size); |
143 | |
|
144 | 0 | if (cwd_buffer == NULL) |
145 | 0 | return -1; |
146 | | |
147 | 0 | git_fs_path_mkposix(buffer_out); |
148 | 0 | git_fs_path_string_to_dir(buffer_out, size); /* append trailing slash */ |
149 | |
|
150 | 0 | return 0; |
151 | 0 | } |
152 | | |
153 | | int p_rename(const char *from, const char *to) |
154 | 6 | { |
155 | 6 | if (!link(from, to)) { |
156 | 0 | p_unlink(from); |
157 | 0 | return 0; |
158 | 0 | } |
159 | | |
160 | 6 | if (!rename(from, to)) |
161 | 6 | return 0; |
162 | | |
163 | 0 | return -1; |
164 | 6 | } |
165 | | |
166 | | #endif /* GIT_WIN32 */ |
167 | | |
168 | | ssize_t p_read(git_file fd, void *buf, size_t cnt) |
169 | 2.55k | { |
170 | 2.55k | char *b = buf; |
171 | | |
172 | 2.55k | if (!git__is_ssizet(cnt)) { |
173 | | #ifdef GIT_WIN32 |
174 | | SetLastError(ERROR_INVALID_PARAMETER); |
175 | | #endif |
176 | 0 | errno = EINVAL; |
177 | 0 | return -1; |
178 | 0 | } |
179 | | |
180 | 4.92k | while (cnt) { |
181 | 2.36k | ssize_t r; |
182 | | #ifdef GIT_WIN32 |
183 | | r = read(fd, b, cnt > INT_MAX ? INT_MAX : (unsigned int)cnt); |
184 | | #else |
185 | 2.36k | r = read(fd, b, cnt); |
186 | 2.36k | #endif |
187 | 2.36k | if (r < 0) { |
188 | 0 | if (errno == EINTR || errno == EAGAIN) |
189 | 0 | continue; |
190 | 0 | return -1; |
191 | 0 | } |
192 | 2.36k | if (!r) |
193 | 0 | break; |
194 | 2.36k | cnt -= r; |
195 | 2.36k | b += r; |
196 | 2.36k | } |
197 | 2.55k | return (b - (char *)buf); |
198 | 2.55k | } |
199 | | |
200 | | int p_write(git_file fd, const void *buf, size_t cnt) |
201 | 16 | { |
202 | 16 | const char *b = buf; |
203 | | |
204 | 32 | while (cnt) { |
205 | 16 | ssize_t r; |
206 | | #ifdef GIT_WIN32 |
207 | | GIT_ASSERT((size_t)((unsigned int)cnt) == cnt); |
208 | | r = write(fd, b, (unsigned int)cnt); |
209 | | #else |
210 | 16 | r = write(fd, b, cnt); |
211 | 16 | #endif |
212 | 16 | if (r < 0) { |
213 | 0 | if (errno == EINTR || GIT_ISBLOCKED(errno)) |
214 | 0 | continue; |
215 | 0 | return -1; |
216 | 0 | } |
217 | 16 | if (!r) { |
218 | 0 | errno = EPIPE; |
219 | 0 | return -1; |
220 | 0 | } |
221 | 16 | cnt -= r; |
222 | 16 | b += r; |
223 | 16 | } |
224 | 16 | return 0; |
225 | 16 | } |
226 | | |
227 | | #ifdef NO_MMAP |
228 | | |
229 | | #include "map.h" |
230 | | |
231 | | int git__page_size(size_t *page_size) |
232 | | { |
233 | | /* dummy; here we don't need any alignment anyway */ |
234 | | *page_size = 4096; |
235 | | return 0; |
236 | | } |
237 | | |
238 | | int git__mmap_alignment(size_t *alignment) |
239 | | { |
240 | | /* dummy; here we don't need any alignment anyway */ |
241 | | *alignment = 4096; |
242 | | return 0; |
243 | | } |
244 | | |
245 | | |
246 | | int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, off64_t offset) |
247 | | { |
248 | | const char *ptr; |
249 | | size_t remaining_len; |
250 | | |
251 | | GIT_MMAP_VALIDATE(out, len, prot, flags); |
252 | | |
253 | | /* writes cannot be emulated without handling pagefaults since write happens by |
254 | | * writing to mapped memory */ |
255 | | if (prot & GIT_PROT_WRITE) { |
256 | | git_error_set(GIT_ERROR_OS, "trying to map %s-writeable", |
257 | | ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED) ? "shared": "private"); |
258 | | return -1; |
259 | | } |
260 | | |
261 | | if (!git__is_ssizet(len)) { |
262 | | errno = EINVAL; |
263 | | return -1; |
264 | | } |
265 | | |
266 | | out->len = 0; |
267 | | out->data = git__malloc(len); |
268 | | GIT_ERROR_CHECK_ALLOC(out->data); |
269 | | |
270 | | remaining_len = len; |
271 | | ptr = (const char *)out->data; |
272 | | while (remaining_len > 0) { |
273 | | ssize_t nb; |
274 | | HANDLE_EINTR(nb, p_pread(fd, (void *)ptr, remaining_len, offset)); |
275 | | if (nb <= 0) { |
276 | | git_error_set(GIT_ERROR_OS, "mmap emulation failed"); |
277 | | git__free(out->data); |
278 | | out->data = NULL; |
279 | | return -1; |
280 | | } |
281 | | |
282 | | ptr += nb; |
283 | | offset += nb; |
284 | | remaining_len -= nb; |
285 | | } |
286 | | |
287 | | out->len = len; |
288 | | return 0; |
289 | | } |
290 | | |
291 | | int p_munmap(git_map *map) |
292 | | { |
293 | | GIT_ASSERT_ARG(map); |
294 | | git__free(map->data); |
295 | | |
296 | | /* Initializing will help debug use-after-free */ |
297 | | map->len = 0; |
298 | | map->data = NULL; |
299 | | |
300 | | return 0; |
301 | | } |
302 | | |
303 | | #endif |
304 | | |
305 | | #if defined(GIT_IO_POLL) || defined(GIT_IO_WSAPOLL) |
306 | | |
307 | | /* Handled by posix.h; this test simplifies the final else */ |
308 | | |
309 | | #elif defined(GIT_IO_SELECT) |
310 | | |
311 | | int p_poll(struct pollfd *fds, unsigned int nfds, int timeout_ms) |
312 | | { |
313 | | fd_set read_fds, write_fds, except_fds; |
314 | | struct timeval timeout = { 0, 0 }; |
315 | | unsigned int i; |
316 | | int max_fd = -1, ret; |
317 | | |
318 | | FD_ZERO(&read_fds); |
319 | | FD_ZERO(&write_fds); |
320 | | FD_ZERO(&except_fds); |
321 | | |
322 | | for (i = 0; i < nfds; i++) { |
323 | | if ((fds[i].events & POLLIN)) |
324 | | FD_SET(fds[i].fd, &read_fds); |
325 | | |
326 | | if ((fds[i].events & POLLOUT)) |
327 | | FD_SET(fds[i].fd, &write_fds); |
328 | | |
329 | | if ((fds[i].events & POLLPRI)) |
330 | | FD_SET(fds[i].fd, &except_fds); |
331 | | |
332 | | max_fd = MAX(max_fd, fds[i].fd); |
333 | | } |
334 | | |
335 | | if (timeout_ms > 0) { |
336 | | timeout.tv_sec = timeout_ms / 1000; |
337 | | timeout.tv_usec = (timeout_ms % 1000) * 1000; |
338 | | } |
339 | | |
340 | | if ((ret = select(max_fd + 1, &read_fds, &write_fds, &except_fds, |
341 | | timeout_ms < 0 ? NULL : &timeout)) < 0) |
342 | | goto done; |
343 | | |
344 | | for (i = 0; i < nfds; i++) { |
345 | | fds[i].revents = 0 | |
346 | | FD_ISSET(fds[i].fd, &read_fds) ? POLLIN : 0 | |
347 | | FD_ISSET(fds[i].fd, &write_fds) ? POLLOUT : 0 | |
348 | | FD_ISSET(fds[i].fd, &except_fds) ? POLLPRI : 0; |
349 | | } |
350 | | |
351 | | done: |
352 | | return ret; |
353 | | } |
354 | | |
355 | | #else |
356 | | # error no poll compatible implementation |
357 | | #endif |