file_util.c: refactor and fix checksum_hex2bin()
[project/opkg-lede.git] / libopkg / file_util.c
1 /* file_util.c - convenience routines for common stat operations
2
3 Copyright (C) 2009 Ubiq Technologies <graham.gower@gmail.com>
4
5 Carl D. Worth
6 Copyright (C) 2001 University of Southern California
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2, or (at
11 your option) any later version.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17 */
18
19 #include <stdio.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <dirent.h>
23 #include <unistd.h>
24 #include <ctype.h>
25
26 #include "sprintf_alloc.h"
27 #include "file_util.h"
28 #include <libubox/md5.h>
29 #include "libbb/libbb.h"
30
31 #include "sha256.h"
32
33 int file_exists(const char *file_name)
34 {
35 struct stat st;
36
37 if (stat(file_name, &st) == -1)
38 return 0;
39
40 return 1;
41 }
42
43 int file_is_dir(const char *file_name)
44 {
45 struct stat st;
46
47 if (stat(file_name, &st) == -1)
48 return 0;
49
50 return S_ISDIR(st.st_mode);
51 }
52
53 /* read a single line from a file, stopping at a newline or EOF.
54 If a newline is read, it will appear in the resulting string.
55 Return value is a malloc'ed char * which should be freed at
56 some point by the caller.
57
58 Return value is NULL if the file is at EOF when called.
59 */
60 char *file_read_line_alloc(FILE * fp)
61 {
62 size_t buf_len, line_size;
63 char buf[BUFSIZ];
64 char *line = NULL;
65 int got_nl = 0;
66
67 while (fgets(buf, BUFSIZ, fp)) {
68 buf_len = strlen(buf);
69 if (buf_len > 0 && buf[buf_len - 1] == '\n') {
70 buf_len--;
71 buf[buf_len] = '\0';
72 got_nl = 1;
73 }
74 if (line) {
75 line_size += buf_len;
76 line = xrealloc(line, line_size + 1);
77 strncat(line, buf, line_size);
78 } else {
79 line_size = buf_len + 1;
80 line = xstrdup(buf);
81 }
82 if (got_nl)
83 break;
84 }
85
86 return line;
87 }
88
89 int file_move(const char *src, const char *dest)
90 {
91 int err;
92
93 err = rename(src, dest);
94 if (err == -1) {
95 if (errno == EXDEV) {
96 /* src & dest live on different file systems */
97 err = file_copy(src, dest);
98 if (err == 0)
99 unlink(src);
100 } else {
101 opkg_perror(ERROR, "Failed to rename %s to %s",
102 src, dest);
103 }
104 }
105
106 return err;
107 }
108
109 int file_copy(const char *src, const char *dest)
110 {
111 int err;
112
113 err = copy_file(src, dest, FILEUTILS_FORCE | FILEUTILS_PRESERVE_STATUS);
114 if (err)
115 opkg_msg(ERROR, "Failed to copy file %s to %s.\n", src, dest);
116
117 return err;
118 }
119
120 int file_mkdir_hier(const char *path, long mode)
121 {
122 return make_directory(path, mode, FILEUTILS_RECUR);
123 }
124
125
126 static int hex2bin(unsigned char x)
127 {
128 if (x >= 'a' && x <= 'f')
129 return x - 'a' + 10;
130 else if (x >= 'A' && x <= 'F')
131 return x - 'A' + 10;
132 else if (x >= '0' && x <= '9')
133 return x - '0';
134 else
135 return 0;
136 }
137
138 static const unsigned char bin2hex[16] = {
139 '0', '1', '2', '3',
140 '4', '5', '6', '7',
141 '8', '9', 'a', 'b',
142 'c', 'd', 'e', 'f'
143 };
144
145 char *file_md5sum_alloc(const char *file_name)
146 {
147 static const int md5sum_bin_len = 16;
148 static const int md5sum_hex_len = 32;
149
150 int i, len;
151 char *md5sum_hex;
152 unsigned char md5sum_bin[md5sum_bin_len];
153
154 len = md5sum(file_name, md5sum_bin);
155
156 if (len) {
157 opkg_msg(ERROR, "Could't compute md5sum for %s.\n", file_name);
158 return NULL;
159 }
160
161 md5sum_hex = xcalloc(1, md5sum_hex_len + 1);
162
163 for (i = 0; i < md5sum_bin_len; i++) {
164 md5sum_hex[i * 2] = bin2hex[md5sum_bin[i] >> 4];
165 md5sum_hex[i * 2 + 1] = bin2hex[md5sum_bin[i] & 0xf];
166 }
167
168 md5sum_hex[md5sum_hex_len] = '\0';
169
170 return md5sum_hex;
171 }
172
173 char *file_sha256sum_alloc(const char *file_name)
174 {
175 static const int sha256sum_bin_len = 32;
176 static const int sha256sum_hex_len = 64;
177
178 int i, err;
179 FILE *file;
180 char *sha256sum_hex;
181 unsigned char sha256sum_bin[sha256sum_bin_len];
182
183 sha256sum_hex = xcalloc(1, sha256sum_hex_len + 1);
184
185 file = fopen(file_name, "r");
186 if (file == NULL) {
187 opkg_perror(ERROR, "Failed to open file %s", file_name);
188 free(sha256sum_hex);
189 return NULL;
190 }
191
192 err = sha256_stream(file, sha256sum_bin);
193 if (err) {
194 opkg_msg(ERROR, "Could't compute sha256sum for %s.\n",
195 file_name);
196 fclose(file);
197 free(sha256sum_hex);
198 return NULL;
199 }
200
201 fclose(file);
202
203 for (i = 0; i < sha256sum_bin_len; i++) {
204 sha256sum_hex[i * 2] = bin2hex[sha256sum_bin[i] >> 4];
205 sha256sum_hex[i * 2 + 1] = bin2hex[sha256sum_bin[i] & 0xf];
206 }
207
208 sha256sum_hex[sha256sum_hex_len] = '\0';
209
210 return sha256sum_hex;
211 }
212
213 char *checksum_bin2hex(const char *src, size_t len)
214 {
215 unsigned char *p;
216 static unsigned char buf[65];
217 const unsigned char *s = (unsigned char *)src;
218 if (!s || len > 32)
219 return NULL;
220
221 for (p = buf; len > 0; s++, len--) {
222 *p++ = bin2hex[*s / 16];
223 *p++ = bin2hex[*s % 16];
224 }
225
226 *p = 0;
227
228 return (char *)buf;
229 }
230
231 char *checksum_hex2bin(const char *src, size_t *len)
232 {
233 static unsigned char buf[32];
234 size_t n = 0;
235
236 *len = 0;
237
238 if (!src)
239 return NULL;
240
241 while (isspace(*src))
242 src++;
243
244 if (strlen(src) > sizeof(buf) * 2)
245 return NULL;
246
247 while (*src) {
248 if (n >= sizeof(buf) || !isxdigit(src[0]) || !isxdigit(src[1]))
249 return NULL;
250
251 buf[n++] = hex2bin(src[0]) * 16 + hex2bin(src[1]);
252 src += 2;
253 }
254
255 *len = n;
256 return n ? (char *)buf : NULL;
257 }
258
259 int rm_r(const char *path)
260 {
261 int ret = 0;
262 DIR *dir;
263 struct dirent *dent;
264
265 if (path == NULL) {
266 opkg_perror(ERROR, "Missing directory parameter");
267 return -1;
268 }
269
270 dir = opendir(path);
271 if (dir == NULL) {
272 opkg_perror(ERROR, "Failed to open dir %s", path);
273 return -1;
274 }
275
276 if (fchdir(dirfd(dir)) == -1) {
277 opkg_perror(ERROR, "Failed to change to dir %s", path);
278 closedir(dir);
279 return -1;
280 }
281
282 while (1) {
283 errno = 0;
284 if ((dent = readdir(dir)) == NULL) {
285 if (errno) {
286 opkg_perror(ERROR, "Failed to read dir %s",
287 path);
288 ret = -1;
289 }
290 break;
291 }
292
293 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
294 continue;
295
296 #ifdef _BSD_SOURCE
297 if (dent->d_type == DT_DIR) {
298 if ((ret = rm_r(dent->d_name)) == -1)
299 break;
300 continue;
301 } else if (dent->d_type == DT_UNKNOWN)
302 #endif
303 {
304 struct stat st;
305 if ((ret = lstat(dent->d_name, &st)) == -1) {
306 opkg_perror(ERROR, "Failed to lstat %s",
307 dent->d_name);
308 break;
309 }
310 if (S_ISDIR(st.st_mode)) {
311 if ((ret = rm_r(dent->d_name)) == -1)
312 break;
313 continue;
314 }
315 }
316
317 if ((ret = unlink(dent->d_name)) == -1) {
318 opkg_perror(ERROR, "Failed to unlink %s", dent->d_name);
319 break;
320 }
321 }
322
323 if (chdir("..") == -1) {
324 ret = -1;
325 opkg_perror(ERROR, "Failed to change to dir %s/..", path);
326 }
327
328 if (rmdir(path) == -1) {
329 ret = -1;
330 opkg_perror(ERROR, "Failed to remove dir %s", path);
331 }
332
333 if (closedir(dir) == -1) {
334 ret = -1;
335 opkg_perror(ERROR, "Failed to close dir %s", path);
336 }
337
338 return ret;
339 }
340
341 static int urlencode_is_specialchar(char c)
342 {
343 switch (c) {
344 case ':':
345 case '?':
346 case '#':
347 case '[':
348 case ']':
349 case '@':
350 case '!':
351 case '$':
352 case '&':
353 case '\'':
354 case '(':
355 case ')':
356 case '*':
357 case '+':
358 case ',':
359 case ';':
360 case '=':
361 case '%':
362 return 1;
363
364 default:
365 return 0;
366 }
367 }
368
369 char *urlencode_path(const char *filename)
370 {
371 size_t len = 0;
372 const unsigned char *in;
373 unsigned char *copy, *out;
374
375 for (in = (unsigned char *)filename; *in != 0; in++)
376 len += urlencode_is_specialchar(*in) ? 3 : 1;
377
378 copy = xcalloc(1, len + 1);
379
380 for (in = (unsigned char *)filename, out = copy; *in != 0; in++) {
381 if (urlencode_is_specialchar(*in)) {
382 *out++ = '%';
383 *out++ = bin2hex[*in / 16];
384 *out++ = bin2hex[*in % 16];
385 }
386 else {
387 *out++ = *in;
388 }
389 }
390
391 return (char *)copy;
392 }
393
394 char *urldecode_path(const char *filename)
395 {
396 unsigned char *copy = (unsigned char *)xstrdup(filename);
397 unsigned char *in, *out;
398
399 for (in = copy, out = copy; *in != 0; in++) {
400 if (*in == '%' && isxdigit(in[1]) && isxdigit(in[2])) {
401 *out++ = hex2bin(in[1]) * 16 + hex2bin(in[2]);
402 in += 2;
403 }
404 else {
405 *out++ = *in;
406 }
407 }
408
409 *out = 0;
410
411 return (char *)copy;
412 }