Coverage Report

Created: 2025-12-31 06:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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