Coverage Report

Created: 2025-07-11 06:45

/src/minizip-ng/mz_os_posix.c
Line
Count
Source (jump to first uncovered line)
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_strm.h"
13
#include "mz_os.h"
14
15
#include <stdio.h> /* rename */
16
#include <errno.h>
17
#if defined(HAVE_ICONV)
18
#  include <iconv.h>
19
#endif
20
#include <string.h>
21
#include <sys/types.h>
22
#include <sys/stat.h>
23
24
#ifndef _WIN32
25
#  include <utime.h>
26
#  include <unistd.h>
27
#endif
28
#if defined(__APPLE__)
29
#  include <mach/clock.h>
30
#  include <mach/mach.h>
31
#endif
32
33
#if defined(HAVE_GETRANDOM)
34
#  include <sys/random.h>
35
#endif
36
#if defined(HAVE_LIBBSD)
37
#  include <stdlib.h> /* arc4random_buf */
38
#endif
39
40
#ifndef MZ_PRESERVE_NATIVE_STRUCTURE
41
#  define MZ_PRESERVE_NATIVE_STRUCTURE 1
42
#endif
43
44
/***************************************************************************/
45
46
#if defined(HAVE_ICONV)
47
0
char *mz_os_utf8_string_create(const char *string, int32_t encoding) {
48
0
    iconv_t cd;
49
    /// up to CP2147483647
50
0
    char string_encoding[13];
51
0
    const char *from_encoding = NULL;
52
0
    size_t result = 0;
53
0
    size_t string_length = 0;
54
0
    size_t string_utf8_size = 0;
55
0
    char *string_utf8 = NULL;
56
0
    char *string_utf8_ptr = NULL;
57
58
0
    if (!string || encoding <= 0)
59
0
        return NULL;
60
61
0
    if (encoding == MZ_ENCODING_UTF8)
62
0
        from_encoding = "UTF-8";
63
0
    else {
64
0
        snprintf(string_encoding, sizeof(string_encoding), "CP%03" PRId32, encoding);
65
0
        from_encoding = string_encoding;
66
0
    }
67
68
0
    cd = iconv_open("UTF-8", from_encoding);
69
0
    if (cd == (iconv_t)-1)
70
0
        return NULL;
71
72
0
    string_length = strlen(string);
73
0
    string_utf8_size = string_length * 2;
74
0
    string_utf8 = (char *)calloc((int32_t)(string_utf8_size + 1), sizeof(char));
75
0
    string_utf8_ptr = string_utf8;
76
77
0
    if (string_utf8) {
78
0
        result = iconv(cd, (char **)&string, &string_length, (char **)&string_utf8_ptr, &string_utf8_size);
79
0
    }
80
81
0
    iconv_close(cd);
82
83
0
    if (result == (size_t)-1) {
84
0
        free(string_utf8);
85
0
        string_utf8 = NULL;
86
0
    }
87
88
0
    return string_utf8;
89
0
}
90
#else
91
char *mz_os_utf8_string_create(const char *string, int32_t encoding) {
92
    return strdup(string);
93
}
94
#endif
95
96
0
void mz_os_utf8_string_delete(char **string) {
97
0
    if (string) {
98
0
        free(*string);
99
0
        *string = NULL;
100
0
    }
101
0
}
102
103
/***************************************************************************/
104
105
#if defined(HAVE_GETRANDOM)
106
int32_t mz_os_rand(uint8_t *buf, int32_t size) {
107
    int32_t left = size;
108
    int32_t written = 0;
109
110
    while (left > 0) {
111
        written = getrandom(buf, left, 0);
112
        if (written < 0)
113
            return MZ_INTERNAL_ERROR;
114
115
        buf += written;
116
        left -= written;
117
    }
118
    return size - left;
119
}
120
#elif defined(HAVE_ARC4RANDOM_BUF)
121
int32_t mz_os_rand(uint8_t *buf, int32_t size) {
122
    if (size < 0)
123
        return 0;
124
    arc4random_buf(buf, (uint32_t)size);
125
    return size;
126
}
127
#elif defined(HAVE_ARC4RANDOM)
128
int32_t mz_os_rand(uint8_t *buf, int32_t size) {
129
    int32_t left = size;
130
    for (; left > 2; left -= 3, buf += 3) {
131
        uint32_t val = arc4random();
132
133
        buf[0] = (val) & 0xFF;
134
        buf[1] = (val >> 8) & 0xFF;
135
        buf[2] = (val >> 16) & 0xFF;
136
    }
137
    for (; left > 0; left--, buf++) {
138
        *buf = arc4random() & 0xFF;
139
    }
140
    return size - left;
141
}
142
#else
143
0
int32_t mz_os_rand(uint8_t *buf, int32_t size) {
144
0
    static unsigned calls = 0;
145
0
    int32_t i = 0;
146
147
    /* Ensure different random header each time */
148
0
    if (++calls == 1) {
149
0
#  define PI_SEED 3141592654UL
150
0
        srand((unsigned)(time(NULL) ^ PI_SEED));
151
0
    }
152
153
0
    while (i < size)
154
0
        buf[i++] = (rand() >> 7) & 0xff;
155
156
0
    return size;
157
0
}
158
#endif
159
160
0
int32_t mz_os_rename(const char *source_path, const char *target_path) {
161
0
    if (rename(source_path, target_path) == -1)
162
0
        return MZ_EXIST_ERROR;
163
164
0
    return MZ_OK;
165
0
}
166
167
0
int32_t mz_os_unlink(const char *path) {
168
0
    if (unlink(path) == -1)
169
0
        return MZ_EXIST_ERROR;
170
171
0
    return MZ_OK;
172
0
}
173
174
0
int32_t mz_os_file_exists(const char *path) {
175
0
    struct stat path_stat;
176
177
0
    memset(&path_stat, 0, sizeof(path_stat));
178
0
    if (stat(path, &path_stat) == 0)
179
0
        return MZ_OK;
180
0
    return MZ_EXIST_ERROR;
181
0
}
182
183
0
int64_t mz_os_get_file_size(const char *path) {
184
0
    struct stat path_stat;
185
186
0
    memset(&path_stat, 0, sizeof(path_stat));
187
0
    if (stat(path, &path_stat) == 0) {
188
        /* Stat returns size taken up by directory entry, so return 0 */
189
0
        if (S_ISDIR(path_stat.st_mode))
190
0
            return 0;
191
192
0
        return path_stat.st_size;
193
0
    }
194
195
0
    return 0;
196
0
}
197
198
0
int32_t mz_os_get_file_date(const char *path, time_t *modified_date, time_t *accessed_date, time_t *creation_date) {
199
0
    struct stat path_stat;
200
0
    char *name = NULL;
201
0
    int32_t err = MZ_INTERNAL_ERROR;
202
203
0
    memset(&path_stat, 0, sizeof(path_stat));
204
205
0
    if (strcmp(path, "-") != 0) {
206
        /* Not all systems allow stat'ing a file with / appended */
207
0
        name = strdup(path);
208
0
        mz_path_remove_slash(name);
209
210
0
        if (stat(name, &path_stat) == 0) {
211
0
            if (modified_date)
212
0
                *modified_date = path_stat.st_mtime;
213
0
            if (accessed_date)
214
0
                *accessed_date = path_stat.st_atime;
215
            /* Creation date not supported */
216
0
            if (creation_date)
217
0
                *creation_date = 0;
218
219
0
            err = MZ_OK;
220
0
        }
221
222
0
        free(name);
223
0
    }
224
225
0
    return err;
226
0
}
227
228
0
int32_t mz_os_set_file_date(const char *path, time_t modified_date, time_t accessed_date, time_t creation_date) {
229
0
    struct utimbuf ut;
230
231
0
    ut.actime = accessed_date;
232
0
    ut.modtime = modified_date;
233
234
    /* Creation date not supported */
235
0
    MZ_UNUSED(creation_date);
236
237
0
    if (utime(path, &ut) != 0)
238
0
        return MZ_INTERNAL_ERROR;
239
240
0
    return MZ_OK;
241
0
}
242
243
0
int32_t mz_os_get_file_attribs(const char *path, uint32_t *attributes) {
244
0
    struct stat path_stat;
245
0
    int32_t err = MZ_OK;
246
247
0
    memset(&path_stat, 0, sizeof(path_stat));
248
0
    if (lstat(path, &path_stat) == -1)
249
0
        err = MZ_INTERNAL_ERROR;
250
0
    *attributes = path_stat.st_mode;
251
0
    return err;
252
0
}
253
254
0
int32_t mz_os_set_file_attribs(const char *path, uint32_t attributes) {
255
0
    int32_t err = MZ_OK;
256
257
0
    if (chmod(path, (mode_t)attributes) == -1)
258
0
        err = MZ_INTERNAL_ERROR;
259
260
0
    return err;
261
0
}
262
263
0
int32_t mz_os_make_dir(const char *path) {
264
0
    int32_t err = 0;
265
266
0
    err = mkdir(path, 0755);
267
268
0
    if (err != 0 && errno != EEXIST)
269
0
        return MZ_INTERNAL_ERROR;
270
271
0
    return MZ_OK;
272
0
}
273
274
0
DIR *mz_os_open_dir(const char *path) {
275
0
    return opendir(path);
276
0
}
277
278
0
struct dirent *mz_os_read_dir(DIR *dir) {
279
0
    if (!dir)
280
0
        return NULL;
281
0
    return readdir(dir);
282
0
}
283
284
0
int32_t mz_os_close_dir(DIR *dir) {
285
0
    if (!dir)
286
0
        return MZ_PARAM_ERROR;
287
0
    if (closedir(dir) == -1)
288
0
        return MZ_INTERNAL_ERROR;
289
0
    return MZ_OK;
290
0
}
291
292
7.34k
int32_t mz_os_is_dir_separator(const char c) {
293
7.34k
#if MZ_PRESERVE_NATIVE_STRUCTURE
294
    // While not strictly adhering to 4.4.17.1,
295
    // this preserves UNIX filesystem structure.
296
7.34k
    return c == '/';
297
#else
298
    // While strictly adhering to 4.4.17.1,
299
    // this corrupts UNIX filesystem structure (a filename with a '\\' will become a folder + a file).
300
    return c == '\\' || c == '/';
301
#endif
302
7.34k
}
303
304
0
int32_t mz_os_is_dir(const char *path) {
305
0
    struct stat path_stat;
306
307
0
    memset(&path_stat, 0, sizeof(path_stat));
308
0
    stat(path, &path_stat);
309
0
    if (S_ISDIR(path_stat.st_mode))
310
0
        return MZ_OK;
311
312
0
    return MZ_EXIST_ERROR;
313
0
}
314
315
0
int32_t mz_os_is_symlink(const char *path) {
316
0
    struct stat path_stat;
317
318
0
    memset(&path_stat, 0, sizeof(path_stat));
319
0
    lstat(path, &path_stat);
320
0
    if (S_ISLNK(path_stat.st_mode))
321
0
        return MZ_OK;
322
323
0
    return MZ_EXIST_ERROR;
324
0
}
325
326
0
int32_t mz_os_make_symlink(const char *path, const char *target_path) {
327
0
    if (symlink(target_path, path) != 0)
328
0
        return MZ_INTERNAL_ERROR;
329
0
    return MZ_OK;
330
0
}
331
332
0
int32_t mz_os_read_symlink(const char *path, char *target_path, int32_t max_target_path) {
333
0
    size_t length = 0;
334
335
0
    length = (size_t)readlink(path, target_path, max_target_path - 1);
336
0
    if (length == (size_t)-1)
337
0
        return MZ_EXIST_ERROR;
338
339
0
    target_path[length] = 0;
340
0
    return MZ_OK;
341
0
}
342
343
0
uint64_t mz_os_ms_time(void) {
344
0
    struct timespec ts;
345
346
#if defined(__APPLE__)
347
    clock_serv_t cclock;
348
    mach_timespec_t mts;
349
350
    host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
351
    clock_get_time(cclock, &mts);
352
    mach_port_deallocate(mach_task_self(), cclock);
353
354
    ts.tv_sec = mts.tv_sec;
355
    ts.tv_nsec = mts.tv_nsec;
356
#elif !defined(_POSIX_MONOTONIC_CLOCK) || _POSIX_MONOTONIC_CLOCK < 0
357
    clock_gettime(CLOCK_REALTIME, &ts);
358
#elif _POSIX_MONOTONIC_CLOCK > 0
359
    clock_gettime(CLOCK_MONOTONIC, &ts);
360
#else
361
0
    if (sysconf(_SC_MONOTONIC_CLOCK) > 0)
362
0
        clock_gettime(CLOCK_MONOTONIC, &ts);
363
0
    else
364
0
        clock_gettime(CLOCK_REALTIME, &ts);
365
0
#endif
366
367
0
    return ((uint64_t)ts.tv_sec * 1000) + ((uint64_t)ts.tv_nsec / 1000000);
368
0
}