Coverage Report

Created: 2026-05-27 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/minizip-ng/mz_os_posix.c
Line
Count
Source
1
/* mz_os_posix.c -- System functions for posix
2
   part of the minizip-ng project
3
4
   Copyright (C) Nathan Moinvaziri
5
     https://github.com/zlib-ng/minizip-ng
6
7
   This program is distributed under the terms of the same license as zlib.
8
   See the accompanying LICENSE file for the full text of the license.
9
*/
10
11
#include "mz.h"
12
#include "mz_config.h"
13
#include "mz_strm.h"
14
#include "mz_os.h"
15
16
#include <stdio.h> /* rename */
17
#include <errno.h>
18
#if defined(HAVE_ICONV)
19
#  include <iconv.h>
20
#endif
21
#include <string.h>
22
#include <sys/types.h>
23
#include <sys/stat.h>
24
25
#ifndef _WIN32
26
#  include <utime.h>
27
#  include <unistd.h>
28
#endif
29
#if defined(__APPLE__)
30
#  include <mach/clock.h>
31
#  include <mach/mach.h>
32
#endif
33
34
#if defined(HAVE_GETRANDOM)
35
#  include <sys/random.h>
36
#endif
37
#if defined(HAVE_LIBBSD)
38
#  include <stdlib.h> /* arc4random_buf */
39
#endif
40
41
#ifndef MZ_PRESERVE_NATIVE_STRUCTURE
42
#  define MZ_PRESERVE_NATIVE_STRUCTURE 1
43
#endif
44
45
/***************************************************************************/
46
47
#if defined(HAVE_ICONV)
48
0
char *mz_os_utf8_string_create(const char *string, int32_t encoding) {
49
0
    iconv_t cd;
50
    /// up to CP2147483647
51
0
    char string_encoding[13];
52
0
    const char *from_encoding = NULL;
53
0
    size_t result = 0;
54
0
    size_t string_length = 0;
55
0
    size_t string_utf8_size = 0;
56
0
    char *string_utf8 = NULL;
57
0
    char *string_utf8_ptr = NULL;
58
59
0
    if (!string || encoding <= 0)
60
0
        return NULL;
61
62
0
    if (encoding == MZ_ENCODING_UTF8)
63
0
        from_encoding = "UTF-8";
64
0
    else {
65
0
        snprintf(string_encoding, sizeof(string_encoding), "CP%03" PRId32, encoding);
66
0
        from_encoding = string_encoding;
67
0
    }
68
69
0
    cd = iconv_open("UTF-8", from_encoding);
70
0
    if (cd == (iconv_t)-1)
71
0
        return NULL;
72
73
0
    string_length = strlen(string);
74
0
    string_utf8_size = string_length * 2;
75
0
    string_utf8 = (char *)calloc((int32_t)(string_utf8_size + 1), sizeof(char));
76
0
    string_utf8_ptr = string_utf8;
77
78
0
    if (string_utf8) {
79
0
        result = iconv(cd, (char **)&string, &string_length, (char **)&string_utf8_ptr, &string_utf8_size);
80
0
    }
81
82
0
    iconv_close(cd);
83
84
0
    if (result == (size_t)-1) {
85
0
        free(string_utf8);
86
0
        string_utf8 = NULL;
87
0
    }
88
89
0
    return string_utf8;
90
0
}
91
#else
92
char *mz_os_utf8_string_create(const char *string, int32_t encoding) {
93
    return strdup(string);
94
}
95
#endif
96
97
0
void mz_os_utf8_string_delete(char **string) {
98
0
    if (string) {
99
0
        free(*string);
100
0
        *string = NULL;
101
0
    }
102
0
}
103
104
/***************************************************************************/
105
106
#if defined(HAVE_GETRANDOM)
107
int32_t mz_os_rand(uint8_t *buf, int32_t size) {
108
    int32_t left = size;
109
    int32_t written = 0;
110
111
    while (left > 0) {
112
        written = getrandom(buf, left, 0);
113
        if (written < 0)
114
            return MZ_INTERNAL_ERROR;
115
116
        buf += written;
117
        left -= written;
118
    }
119
    return size - left;
120
}
121
#elif defined(HAVE_ARC4RANDOM_BUF)
122
int32_t mz_os_rand(uint8_t *buf, int32_t size) {
123
    if (size < 0)
124
        return 0;
125
    arc4random_buf(buf, (uint32_t)size);
126
    return size;
127
}
128
#elif defined(HAVE_ARC4RANDOM)
129
int32_t mz_os_rand(uint8_t *buf, int32_t size) {
130
    int32_t left = size;
131
    for (; left > 2; left -= 3, buf += 3) {
132
        uint32_t val = arc4random();
133
134
        buf[0] = (val) & 0xFF;
135
        buf[1] = (val >> 8) & 0xFF;
136
        buf[2] = (val >> 16) & 0xFF;
137
    }
138
    for (; left > 0; left--, buf++) {
139
        *buf = arc4random() & 0xFF;
140
    }
141
    return size - left;
142
}
143
#else
144
0
int32_t mz_os_rand(uint8_t *buf, int32_t size) {
145
0
    static unsigned calls = 0;
146
0
    int32_t i = 0;
147
148
    /* Ensure different random header each time */
149
0
    if (++calls == 1) {
150
0
#  define PI_SEED 3141592654UL
151
0
        srand((unsigned)(time(NULL) ^ PI_SEED));
152
0
    }
153
154
0
    while (i < size)
155
0
        buf[i++] = (rand() >> 7) & 0xff;
156
157
0
    return size;
158
0
}
159
#endif
160
161
0
int32_t mz_os_rename(const char *source_path, const char *target_path) {
162
0
    if (rename(source_path, target_path) == -1)
163
0
        return MZ_EXIST_ERROR;
164
165
0
    return MZ_OK;
166
0
}
167
168
0
int32_t mz_os_path_same_fs(const char *path_a, const char *path_b) {
169
0
    struct stat sa, sb;
170
0
    if (!path_a || !path_b)
171
0
        return MZ_PARAM_ERROR;
172
0
    if (stat(path_a, &sa) != 0 || stat(path_b, &sb) != 0)
173
0
        return MZ_EXIST_ERROR;
174
0
    return (sa.st_dev == sb.st_dev) ? MZ_OK : MZ_EXIST_ERROR;
175
0
}
176
177
0
int32_t mz_os_unlink(const char *path) {
178
0
    if (unlink(path) == -1)
179
0
        return MZ_EXIST_ERROR;
180
181
0
    return MZ_OK;
182
0
}
183
184
0
int32_t mz_os_file_exists(const char *path) {
185
0
    struct stat path_stat;
186
187
0
    memset(&path_stat, 0, sizeof(path_stat));
188
0
    if (stat(path, &path_stat) == 0)
189
0
        return MZ_OK;
190
0
    return MZ_EXIST_ERROR;
191
0
}
192
193
0
int64_t mz_os_get_file_size(const char *path) {
194
0
    struct stat path_stat;
195
196
0
    memset(&path_stat, 0, sizeof(path_stat));
197
0
    if (stat(path, &path_stat) == 0) {
198
        /* Stat returns size taken up by directory entry, so return 0 */
199
0
        if (S_ISDIR(path_stat.st_mode))
200
0
            return 0;
201
202
0
        return path_stat.st_size;
203
0
    }
204
205
0
    return 0;
206
0
}
207
208
0
int32_t mz_os_get_file_date(const char *path, time_t *modified_date, time_t *accessed_date, time_t *creation_date) {
209
0
    struct stat path_stat;
210
0
    char *name = NULL;
211
0
    int32_t err = MZ_INTERNAL_ERROR;
212
213
0
    memset(&path_stat, 0, sizeof(path_stat));
214
215
0
    if (strcmp(path, "-") != 0) {
216
        /* Not all systems allow stat'ing a file with / appended */
217
0
        name = strdup(path);
218
0
        mz_path_remove_slash(name);
219
220
0
        if (stat(name, &path_stat) == 0) {
221
0
            if (modified_date)
222
0
                *modified_date = path_stat.st_mtime;
223
0
            if (accessed_date)
224
0
                *accessed_date = path_stat.st_atime;
225
            /* Creation date not supported */
226
0
            if (creation_date)
227
0
                *creation_date = 0;
228
229
0
            err = MZ_OK;
230
0
        }
231
232
0
        free(name);
233
0
    }
234
235
0
    return err;
236
0
}
237
238
0
int32_t mz_os_set_file_date(const char *path, time_t modified_date, time_t accessed_date, time_t creation_date) {
239
0
    struct utimbuf ut;
240
241
0
    ut.actime = accessed_date;
242
0
    ut.modtime = modified_date;
243
244
    /* Creation date not supported */
245
0
    MZ_UNUSED(creation_date);
246
247
0
    if (utime(path, &ut) != 0)
248
0
        return MZ_INTERNAL_ERROR;
249
250
0
    return MZ_OK;
251
0
}
252
253
0
int32_t mz_os_get_file_attribs(const char *path, uint32_t *attributes) {
254
0
    struct stat path_stat;
255
0
    int32_t err = MZ_OK;
256
257
0
    memset(&path_stat, 0, sizeof(path_stat));
258
0
    if (stat(path, &path_stat) == -1)
259
0
        err = MZ_INTERNAL_ERROR;
260
0
    *attributes = path_stat.st_mode;
261
0
    return err;
262
0
}
263
264
0
int32_t mz_os_set_file_attribs(const char *path, uint32_t attributes) {
265
0
    int32_t err = MZ_OK;
266
267
0
    if (chmod(path, (mode_t)attributes) == -1)
268
0
        err = MZ_INTERNAL_ERROR;
269
270
0
    return err;
271
0
}
272
273
0
int32_t mz_os_make_dir(const char *path) {
274
0
    int32_t err = 0;
275
276
0
    err = mkdir(path, 0755);
277
278
0
    if (err != 0 && errno != EEXIST)
279
0
        return MZ_INTERNAL_ERROR;
280
281
0
    return MZ_OK;
282
0
}
283
284
0
DIR *mz_os_open_dir(const char *path) {
285
0
    return opendir(path);
286
0
}
287
288
0
struct dirent *mz_os_read_dir(DIR *dir) {
289
0
    if (!dir)
290
0
        return NULL;
291
0
    return readdir(dir);
292
0
}
293
294
0
int32_t mz_os_close_dir(DIR *dir) {
295
0
    if (!dir)
296
0
        return MZ_PARAM_ERROR;
297
0
    if (closedir(dir) == -1)
298
0
        return MZ_INTERNAL_ERROR;
299
0
    return MZ_OK;
300
0
}
301
302
5.38k
int32_t mz_os_is_dir_separator(char c) {
303
5.38k
#if MZ_PRESERVE_NATIVE_STRUCTURE
304
    // While not strictly adhering to 4.4.17.1,
305
    // this preserves UNIX filesystem structure.
306
5.38k
    return c == '/';
307
#else
308
    // While strictly adhering to 4.4.17.1,
309
    // this corrupts UNIX filesystem structure (a filename with a '\\' will become a folder + a file).
310
    return c == '\\' || c == '/';
311
#endif
312
5.38k
}
313
314
0
int32_t mz_os_is_dir(const char *path) {
315
0
    struct stat path_stat;
316
317
0
    memset(&path_stat, 0, sizeof(path_stat));
318
0
    stat(path, &path_stat);
319
0
    if (S_ISDIR(path_stat.st_mode))
320
0
        return MZ_OK;
321
322
0
    return MZ_EXIST_ERROR;
323
0
}
324
325
0
int32_t mz_os_is_symlink(const char *path) {
326
0
    struct stat path_stat;
327
328
0
    memset(&path_stat, 0, sizeof(path_stat));
329
0
    lstat(path, &path_stat);
330
0
    if (S_ISLNK(path_stat.st_mode))
331
0
        return MZ_OK;
332
333
0
    return MZ_EXIST_ERROR;
334
0
}
335
336
0
int32_t mz_os_get_link_attribs(const char *path, uint32_t *attributes) {
337
0
    struct stat path_stat;
338
0
    int32_t err = MZ_OK;
339
340
0
    memset(&path_stat, 0, sizeof(path_stat));
341
0
    if (lstat(path, &path_stat) == -1)
342
0
        err = MZ_INTERNAL_ERROR;
343
0
    *attributes = path_stat.st_mode;
344
0
    return err;
345
0
}
346
347
0
int32_t mz_os_make_symlink(const char *path, const char *target_path) {
348
#if !HAVE_SYMLINK
349
    return MZ_SUPPORT_ERROR;
350
#else
351
0
    if (symlink(target_path, path) != 0)
352
0
        return MZ_INTERNAL_ERROR;
353
0
    return MZ_OK;
354
0
#endif
355
0
}
356
357
0
int32_t mz_os_read_symlink(const char *path, char *target_path, int32_t max_target_path) {
358
#if !HAVE_READLINK
359
    return MZ_SUPPORT_ERROR;
360
#else
361
0
    size_t length = 0;
362
363
0
    length = (size_t)readlink(path, target_path, max_target_path - 1);
364
0
    if (length == (size_t)-1)
365
0
        return MZ_EXIST_ERROR;
366
0
    if (length >= (size_t)(max_target_path - 1))
367
0
        return MZ_BUF_ERROR;
368
369
0
    target_path[length] = 0;
370
0
    return MZ_OK;
371
0
#endif
372
0
}
373
374
0
int32_t mz_os_get_temp_path(char *path, int32_t max_path, const char *prefix) {
375
0
    const char *tmp_dir = NULL;
376
0
    char *temp_path;
377
0
    int32_t result = 0;
378
379
0
    if (!path || max_path <= 0)
380
0
        return MZ_PARAM_ERROR;
381
382
0
    tmp_dir = getenv("TMPDIR");
383
0
    if (!tmp_dir)
384
0
        tmp_dir = getenv("TMP");
385
0
    if (!tmp_dir)
386
0
        tmp_dir = getenv("TEMP");
387
0
    if (!tmp_dir)
388
0
        tmp_dir = "/tmp";
389
390
    /* Construct path for mkdtemp in the form <tmp_dir>/<prefix>XXXXXX */
391
0
    temp_path = (char *)calloc(max_path, sizeof(char));
392
0
    if (!temp_path)
393
0
        return MZ_MEM_ERROR;
394
395
    /* mkdtemp replaces XXXXXX with unique characters */
396
0
    result = snprintf(temp_path, max_path, "%s/%sXXXXXX", tmp_dir, prefix ? prefix : "");
397
0
    if (result < 0 || result >= max_path) {
398
0
        free(temp_path);
399
0
        return MZ_BUF_ERROR;
400
0
    }
401
402
    /* Create a temporary directory. */
403
0
    if (!mkdtemp(temp_path)) {
404
0
        free(temp_path);
405
0
        return MZ_INTERNAL_ERROR;
406
0
    }
407
408
    /* Create a filename inside the temporary directory using current time */
409
0
    result = snprintf(path, max_path, "%s/%lux", temp_path, time(NULL));
410
0
    if (result < 0 || result >= max_path) {
411
0
        rmdir(temp_path);
412
0
        free(temp_path);
413
0
        return MZ_BUF_ERROR;
414
0
    }
415
416
0
    free(temp_path);
417
0
    return MZ_OK;
418
0
}
419
420
0
uint64_t mz_os_ms_time(void) {
421
0
    struct timespec ts;
422
423
#if defined(__APPLE__)
424
    clock_serv_t cclock;
425
    mach_timespec_t mts;
426
427
    host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
428
    clock_get_time(cclock, &mts);
429
    mach_port_deallocate(mach_task_self(), cclock);
430
431
    ts.tv_sec = mts.tv_sec;
432
    ts.tv_nsec = mts.tv_nsec;
433
#elif !defined(_POSIX_MONOTONIC_CLOCK) || _POSIX_MONOTONIC_CLOCK < 0
434
    clock_gettime(CLOCK_REALTIME, &ts);
435
#elif _POSIX_MONOTONIC_CLOCK > 0
436
    clock_gettime(CLOCK_MONOTONIC, &ts);
437
#else
438
0
    if (sysconf(_SC_MONOTONIC_CLOCK) > 0)
439
0
        clock_gettime(CLOCK_MONOTONIC, &ts);
440
0
    else
441
0
        clock_gettime(CLOCK_REALTIME, &ts);
442
0
#endif
443
444
0
    return ((uint64_t)ts.tv_sec * 1000) + ((uint64_t)ts.tv_nsec / 1000000);
445
0
}