Line | Count | Source |
1 | | /* mz_os.c -- System functions |
2 | | part of the minizip-ng project |
3 | | |
4 | | Copyright (C) Nathan Moinvaziri |
5 | | https://github.com/zlib-ng/minizip-ng |
6 | | Copyright (C) 1998-2010 Gilles Vollant |
7 | | https://www.winimage.com/zLibDll/minizip.html |
8 | | |
9 | | This program is distributed under the terms of the same license as zlib. |
10 | | See the accompanying LICENSE file for the full text of the license. |
11 | | */ |
12 | | |
13 | | #include "mz.h" |
14 | | #include "mz_crypt.h" |
15 | | #include "mz_os.h" |
16 | | #include "mz_strm.h" |
17 | | #include "mz_strm_os.h" |
18 | | |
19 | | #include <ctype.h> /* tolower */ |
20 | | #include <string.h> |
21 | | |
22 | | /***************************************************************************/ |
23 | | |
24 | 0 | int32_t mz_path_combine(char *path, const char *join, int32_t max_path) { |
25 | 0 | int32_t path_len = 0; |
26 | |
|
27 | 0 | if (!path || !join || !max_path) |
28 | 0 | return MZ_PARAM_ERROR; |
29 | | |
30 | 0 | path_len = (int32_t)strlen(path); |
31 | |
|
32 | 0 | if (path_len == 0) { |
33 | 0 | strncpy(path, join, max_path - 1); |
34 | 0 | path[max_path - 1] = 0; |
35 | 0 | } else { |
36 | 0 | mz_path_append_slash(path, max_path, MZ_PATH_SLASH_PLATFORM); |
37 | 0 | path_len = (int32_t)strlen(path); |
38 | 0 | if (max_path > path_len) |
39 | 0 | strncat(path, join, max_path - path_len - 1); |
40 | 0 | } |
41 | |
|
42 | 0 | return MZ_OK; |
43 | 0 | } |
44 | | |
45 | 0 | int32_t mz_path_append_slash(char *path, int32_t max_path, char slash) { |
46 | 0 | int32_t path_len = (int32_t)strlen(path); |
47 | 0 | if ((path_len + 2) >= max_path) |
48 | 0 | return MZ_BUF_ERROR; |
49 | 0 | if (!mz_os_is_dir_separator(path[path_len - 1])) { |
50 | 0 | path[path_len] = slash; |
51 | 0 | path[path_len + 1] = 0; |
52 | 0 | } |
53 | 0 | return MZ_OK; |
54 | 0 | } |
55 | | |
56 | 0 | int32_t mz_path_remove_slash(char *path) { |
57 | 0 | int32_t path_len = (int32_t)strlen(path); |
58 | 0 | while (path_len > 0) { |
59 | 0 | if (mz_os_is_dir_separator(path[path_len - 1])) |
60 | 0 | path[path_len - 1] = 0; |
61 | 0 | else |
62 | 0 | break; |
63 | | |
64 | 0 | path_len -= 1; |
65 | 0 | } |
66 | 0 | return MZ_OK; |
67 | 0 | } |
68 | | |
69 | 0 | int32_t mz_path_has_slash(const char *path) { |
70 | 0 | int32_t path_len = (int32_t)strlen(path); |
71 | 0 | if (path_len > 0 && !mz_os_is_dir_separator(path[path_len - 1])) |
72 | 0 | return MZ_EXIST_ERROR; |
73 | 0 | return MZ_OK; |
74 | 0 | } |
75 | | |
76 | 0 | int32_t mz_path_convert_slashes(char *path, char slash) { |
77 | 0 | int32_t i = 0; |
78 | |
|
79 | 0 | for (i = 0; i < (int32_t)strlen(path); i += 1) { |
80 | 0 | if (mz_os_is_dir_separator(path[i])) |
81 | 0 | path[i] = slash; |
82 | 0 | } |
83 | 0 | return MZ_OK; |
84 | 0 | } |
85 | | |
86 | 0 | int32_t mz_path_compare_wc(const char *path, const char *wildcard, uint8_t ignore_case) { |
87 | 0 | while (*path != 0) { |
88 | 0 | switch (*wildcard) { |
89 | 0 | case '*': |
90 | |
|
91 | 0 | if (*(wildcard + 1) == 0) |
92 | 0 | return MZ_OK; |
93 | | |
94 | 0 | while (*path != 0) { |
95 | 0 | if (mz_path_compare_wc(path, (wildcard + 1), ignore_case) == MZ_OK) |
96 | 0 | return MZ_OK; |
97 | | |
98 | 0 | path += 1; |
99 | 0 | } |
100 | | |
101 | 0 | return MZ_EXIST_ERROR; |
102 | | |
103 | 0 | default: |
104 | | /* Ignore differences in path slashes on platforms */ |
105 | 0 | if ((*path == '\\' && *wildcard == '/') || (*path == '/' && *wildcard == '\\')) |
106 | 0 | break; |
107 | | |
108 | 0 | if (ignore_case) { |
109 | 0 | if (tolower(*path) != tolower(*wildcard)) |
110 | 0 | return MZ_EXIST_ERROR; |
111 | 0 | } else { |
112 | 0 | if (*path != *wildcard) |
113 | 0 | return MZ_EXIST_ERROR; |
114 | 0 | } |
115 | | |
116 | 0 | break; |
117 | 0 | } |
118 | | |
119 | 0 | path += 1; |
120 | 0 | wildcard += 1; |
121 | 0 | } |
122 | | |
123 | 0 | if ((*wildcard != 0) && (*wildcard != '*')) |
124 | 0 | return MZ_EXIST_ERROR; |
125 | | |
126 | 0 | return MZ_OK; |
127 | 0 | } |
128 | | |
129 | 0 | int32_t mz_path_resolve(const char *path, char *output, int32_t max_output) { |
130 | 0 | const char *source = path; |
131 | 0 | const char *check = output; |
132 | 0 | char *target = output; |
133 | |
|
134 | 0 | if (max_output <= 0) |
135 | 0 | return MZ_PARAM_ERROR; |
136 | | |
137 | 0 | while (*source != 0 && max_output > 1) { |
138 | 0 | check = source; |
139 | 0 | if (mz_os_is_dir_separator(*check)) |
140 | 0 | check += 1; |
141 | |
|
142 | 0 | if (source == path || target == output || check != source) { |
143 | | /* Skip double paths */ |
144 | 0 | if (mz_os_is_dir_separator(*check)) { |
145 | 0 | source += 1; |
146 | 0 | continue; |
147 | 0 | } |
148 | 0 | if (*check == '.') { |
149 | 0 | check += 1; |
150 | | |
151 | | /* Remove . if at end of string and not at the beginning */ |
152 | 0 | if (*check == 0 && source != path && target != output) { |
153 | | /* Copy last slash */ |
154 | 0 | *target = *source; |
155 | 0 | target += 1; |
156 | 0 | max_output -= 1; |
157 | 0 | source += (check - source); |
158 | 0 | continue; |
159 | 0 | } |
160 | | /* Remove . if not at end of string */ |
161 | 0 | else if (mz_os_is_dir_separator(*check)) { |
162 | 0 | source += (check - source); |
163 | | /* Skip slash if at beginning of string */ |
164 | 0 | if (target == output && *source != 0) |
165 | 0 | source += 1; |
166 | 0 | continue; |
167 | 0 | } |
168 | | /* Go to parent directory .. */ |
169 | 0 | else if (*check == '.') { |
170 | 0 | check += 1; |
171 | 0 | if (*check == 0 || mz_os_is_dir_separator(*check)) { |
172 | 0 | source += (check - source); |
173 | | |
174 | | /* Search backwards for previous slash or the start of the output string */ |
175 | 0 | if (target != output) { |
176 | 0 | target -= 1; |
177 | 0 | do { |
178 | 0 | if (target == output || mz_os_is_dir_separator(*target)) |
179 | 0 | break; |
180 | | |
181 | 0 | target -= 1; |
182 | 0 | max_output += 1; |
183 | 0 | } while (target > output); |
184 | 0 | } |
185 | |
|
186 | 0 | if ((target == output) && *source != 0) |
187 | 0 | source += 1; |
188 | 0 | if (mz_os_is_dir_separator(*target) && *source == 0) |
189 | 0 | target += 1; |
190 | |
|
191 | 0 | *target = 0; |
192 | 0 | continue; |
193 | 0 | } |
194 | 0 | } |
195 | 0 | } |
196 | 0 | } |
197 | | |
198 | 0 | *target = *source; |
199 | |
|
200 | 0 | source += 1; |
201 | 0 | target += 1; |
202 | 0 | max_output -= 1; |
203 | 0 | } |
204 | |
|
205 | 0 | *target = 0; |
206 | |
|
207 | 0 | if (*path == 0) |
208 | 0 | return MZ_INTERNAL_ERROR; |
209 | | |
210 | 0 | return MZ_OK; |
211 | 0 | } |
212 | | |
213 | 0 | int32_t mz_path_remove_filename(char *path) { |
214 | 0 | char *path_ptr = NULL; |
215 | |
|
216 | 0 | if (!path) |
217 | 0 | return MZ_PARAM_ERROR; |
218 | | |
219 | 0 | path_ptr = path + strlen(path) - 1; |
220 | |
|
221 | 0 | while (path_ptr > path) { |
222 | 0 | if (mz_os_is_dir_separator(*path_ptr)) { |
223 | 0 | *path_ptr = 0; |
224 | 0 | break; |
225 | 0 | } |
226 | | |
227 | 0 | path_ptr -= 1; |
228 | 0 | } |
229 | |
|
230 | 0 | if (path_ptr == path) |
231 | 0 | *path_ptr = 0; |
232 | |
|
233 | 0 | return MZ_OK; |
234 | 0 | } |
235 | | |
236 | 0 | int32_t mz_path_remove_extension(char *path) { |
237 | 0 | char *path_ptr = NULL; |
238 | |
|
239 | 0 | if (!path) |
240 | 0 | return MZ_PARAM_ERROR; |
241 | | |
242 | 0 | path_ptr = path + strlen(path) - 1; |
243 | |
|
244 | 0 | while (path_ptr > path) { |
245 | 0 | if (mz_os_is_dir_separator(*path_ptr)) |
246 | 0 | break; |
247 | 0 | if (*path_ptr == '.') { |
248 | 0 | *path_ptr = 0; |
249 | 0 | break; |
250 | 0 | } |
251 | | |
252 | 0 | path_ptr -= 1; |
253 | 0 | } |
254 | |
|
255 | 0 | if (path_ptr == path) |
256 | 0 | *path_ptr = 0; |
257 | |
|
258 | 0 | return MZ_OK; |
259 | 0 | } |
260 | | |
261 | 0 | int32_t mz_path_get_filename(const char *path, const char **filename) { |
262 | 0 | const char *match = NULL; |
263 | |
|
264 | 0 | if (!path || !filename) |
265 | 0 | return MZ_PARAM_ERROR; |
266 | | |
267 | 0 | *filename = NULL; |
268 | |
|
269 | 0 | for (match = path; *match != 0; match += 1) { |
270 | 0 | if (mz_os_is_dir_separator(*match)) |
271 | 0 | *filename = match + 1; |
272 | 0 | } |
273 | |
|
274 | 0 | if (!*filename) |
275 | 0 | return MZ_EXIST_ERROR; |
276 | | |
277 | 0 | return MZ_OK; |
278 | 0 | } |
279 | | |
280 | 0 | int32_t mz_dir_has_unsafe_symlink(const char *path, const char *base_path) { |
281 | 0 | char *check_path = NULL; |
282 | 0 | char *symlink_target = NULL; |
283 | 0 | char *combined = NULL; |
284 | 0 | char *resolved = NULL; |
285 | 0 | size_t path_len = 0; |
286 | 0 | size_t base_len = 0; |
287 | 0 | size_t max_path = 1024; |
288 | 0 | size_t parent_len = 0; |
289 | 0 | size_t pos = 0; |
290 | 0 | int32_t err = MZ_OK; |
291 | |
|
292 | 0 | if (!path || *path == 0 || !base_path) |
293 | 0 | return MZ_PARAM_ERROR; |
294 | | |
295 | 0 | path_len = strlen(path); |
296 | 0 | base_len = strlen(base_path); |
297 | | |
298 | | /* Remove trailing slash from base_path for comparison */ |
299 | 0 | while (base_len > 0 && mz_os_is_dir_separator(base_path[base_len - 1])) |
300 | 0 | base_len--; |
301 | |
|
302 | 0 | check_path = (char *)calloc(1, path_len + 1); |
303 | 0 | if (!check_path) |
304 | 0 | return MZ_MEM_ERROR; |
305 | | |
306 | | /* Walk through each path component */ |
307 | 0 | while (err == MZ_OK && pos < path_len) { |
308 | | /* Copy separator if present */ |
309 | 0 | if (mz_os_is_dir_separator(path[pos])) { |
310 | 0 | check_path[pos] = path[pos]; |
311 | 0 | pos++; |
312 | 0 | } |
313 | | |
314 | | /* Copy next path component */ |
315 | 0 | while (pos < path_len && !mz_os_is_dir_separator(path[pos])) { |
316 | 0 | check_path[pos] = path[pos]; |
317 | 0 | pos++; |
318 | 0 | } |
319 | 0 | check_path[pos] = 0; |
320 | | |
321 | | /* Check if this existing path component is a symlink */ |
322 | 0 | if (mz_os_is_symlink(check_path) != MZ_OK) |
323 | 0 | continue; |
324 | | |
325 | | /* Allocate symlink buffers on first use */ |
326 | 0 | if (!symlink_target) { |
327 | 0 | symlink_target = (char *)calloc(1, max_path); |
328 | 0 | combined = (char *)calloc(1, max_path); |
329 | 0 | resolved = (char *)calloc(1, max_path); |
330 | |
|
331 | 0 | if (!symlink_target || !combined || !resolved) { |
332 | 0 | err = MZ_MEM_ERROR; |
333 | 0 | break; |
334 | 0 | } |
335 | 0 | } |
336 | | |
337 | 0 | if (mz_os_read_symlink(check_path, symlink_target, max_path) != MZ_OK) { |
338 | 0 | err = MZ_EXIST_ERROR; |
339 | 0 | break; |
340 | 0 | } |
341 | | |
342 | | /* Absolute symlink targets are not allowed */ |
343 | 0 | if (mz_os_is_dir_separator(symlink_target[0])) { |
344 | 0 | err = MZ_EXIST_ERROR; |
345 | 0 | break; |
346 | 0 | } |
347 | | |
348 | | /* Find parent directory length by scanning backwards past filename and trailing slashes */ |
349 | 0 | parent_len = pos; |
350 | 0 | while (parent_len > 0 && !mz_os_is_dir_separator(check_path[parent_len - 1])) |
351 | 0 | parent_len--; |
352 | 0 | while (parent_len > 0 && mz_os_is_dir_separator(check_path[parent_len - 1])) |
353 | 0 | parent_len--; |
354 | | |
355 | | /* Combine parent + symlink_target */ |
356 | 0 | combined[0] = 0; |
357 | 0 | if (parent_len > 0) { |
358 | 0 | strncpy(combined, check_path, parent_len); |
359 | 0 | combined[parent_len] = 0; |
360 | 0 | mz_path_append_slash(combined, (int32_t)max_path, MZ_PATH_SLASH_PLATFORM); |
361 | 0 | } |
362 | 0 | strncat(combined, symlink_target, max_path - strlen(combined) - 1); |
363 | | |
364 | | /* Resolve the combined path to eliminate .. */ |
365 | 0 | if (mz_path_resolve(combined, resolved, (int32_t)max_path) != MZ_OK) { |
366 | 0 | err = MZ_EXIST_ERROR; |
367 | 0 | break; |
368 | 0 | } |
369 | | |
370 | | /* Check that resolved path starts with base_path */ |
371 | 0 | if (strlen(resolved) < base_len || |
372 | 0 | strncmp(resolved, base_path, base_len) != 0 || |
373 | 0 | (resolved[base_len] != 0 && !mz_os_is_dir_separator(resolved[base_len]))) { |
374 | 0 | err = MZ_EXIST_ERROR; |
375 | 0 | break; |
376 | 0 | } |
377 | 0 | } |
378 | |
|
379 | 0 | free(check_path); |
380 | 0 | free(symlink_target); |
381 | 0 | free(combined); |
382 | 0 | free(resolved); |
383 | |
|
384 | 0 | return err; |
385 | 0 | } |
386 | | |
387 | 0 | int32_t mz_dir_make(const char *path) { |
388 | 0 | int32_t err = MZ_OK; |
389 | 0 | char *current_dir = NULL; |
390 | 0 | char *match = NULL; |
391 | 0 | char hold = 0; |
392 | |
|
393 | 0 | if (!*path) |
394 | 0 | return MZ_OK; |
395 | | |
396 | 0 | current_dir = strdup(path); |
397 | 0 | if (!current_dir) |
398 | 0 | return MZ_MEM_ERROR; |
399 | | |
400 | 0 | mz_path_remove_slash(current_dir); |
401 | |
|
402 | 0 | err = mz_os_make_dir(current_dir); |
403 | 0 | if (err != MZ_OK) { |
404 | 0 | match = current_dir + 1; |
405 | 0 | while (1) { |
406 | 0 | while (*match != 0 && !mz_os_is_dir_separator(*match)) |
407 | 0 | match += 1; |
408 | 0 | hold = *match; |
409 | 0 | *match = 0; |
410 | |
|
411 | 0 | err = mz_os_make_dir(current_dir); |
412 | 0 | if (err != MZ_OK) |
413 | 0 | break; |
414 | 0 | if (hold == 0) |
415 | 0 | break; |
416 | | |
417 | 0 | *match = hold; |
418 | 0 | match += 1; |
419 | 0 | } |
420 | 0 | } |
421 | |
|
422 | 0 | free(current_dir); |
423 | 0 | return err; |
424 | 0 | } |
425 | | |
426 | 0 | int32_t mz_file_get_crc(const char *path, uint32_t *result_crc) { |
427 | 0 | void *stream = NULL; |
428 | 0 | uint32_t crc32 = 0; |
429 | 0 | int32_t read = 0; |
430 | 0 | int32_t err = MZ_OK; |
431 | 0 | uint8_t buf[16384]; |
432 | |
|
433 | 0 | stream = mz_stream_os_create(); |
434 | 0 | if (!stream) |
435 | 0 | return MZ_MEM_ERROR; |
436 | | |
437 | 0 | err = mz_stream_os_open(stream, path, MZ_OPEN_MODE_READ); |
438 | 0 | if (err == MZ_OK) { |
439 | 0 | do { |
440 | 0 | read = mz_stream_os_read(stream, buf, sizeof(buf)); |
441 | |
|
442 | 0 | if (read < 0) { |
443 | 0 | err = read; |
444 | 0 | break; |
445 | 0 | } |
446 | | |
447 | 0 | crc32 = mz_crypt_crc32_update(crc32, buf, read); |
448 | 0 | } while ((err == MZ_OK) && (read > 0)); |
449 | |
|
450 | 0 | mz_stream_os_close(stream); |
451 | 0 | } |
452 | |
|
453 | 0 | *result_crc = crc32; |
454 | |
|
455 | 0 | mz_stream_os_delete(&stream); |
456 | |
|
457 | 0 | return err; |
458 | 0 | } |
459 | | |
460 | | /***************************************************************************/ |