c0acec3f8f6abaeb4a69507559a28406ba129f82
[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 size_t slen;
234 unsigned char *p;
235 const unsigned char *s;
236 static unsigned char buf[32];
237
238 if (!src) {
239 *len = 0;
240 return NULL;
241 }
242
243 while (isspace(*src))
244 src++;
245
246 slen = strlen(src);
247
248 if (slen > 64) {
249 *len = 0;
250 return NULL;
251 }
252
253 for (s = (unsigned char *)src, p = buf, *len = 0;
254 slen > 0 && isxdigit(s[0]) && isxdigit(s[1]);
255 slen--, s += 2, (*len)++)
256 *p++ = hex2bin(s[0]) * 16 + hex2bin(s[1]);
257
258 return (char *)buf;
259 }
260
261 int rm_r(const char *path)
262 {
263 int ret = 0;
264 DIR *dir;
265 struct dirent *dent;
266
267 if (path == NULL) {
268 opkg_perror(ERROR, "Missing directory parameter");
269 return -1;
270 }
271
272 dir = opendir(path);
273 if (dir == NULL) {
274 opkg_perror(ERROR, "Failed to open dir %s", path);
275 return -1;
276 }
277
278 if (fchdir(dirfd(dir)) == -1) {
279 opkg_perror(ERROR, "Failed to change to dir %s", path);
280 closedir(dir);
281 return -1;
282 }
283
284 while (1) {
285 errno = 0;
286 if ((dent = readdir(dir)) == NULL) {
287 if (errno) {
288 opkg_perror(ERROR, "Failed to read dir %s",
289 path);
290 ret = -1;
291 }
292 break;
293 }
294
295 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
296 continue;
297
298 #ifdef _BSD_SOURCE
299 if (dent->d_type == DT_DIR) {
300 if ((ret = rm_r(dent->d_name)) == -1)
301 break;
302 continue;
303 } else if (dent->d_type == DT_UNKNOWN)
304 #endif
305 {
306 struct stat st;
307 if ((ret = lstat(dent->d_name, &st)) == -1) {
308 opkg_perror(ERROR, "Failed to lstat %s",
309 dent->d_name);
310 break;
311 }
312 if (S_ISDIR(st.st_mode)) {
313 if ((ret = rm_r(dent->d_name)) == -1)
314 break;
315 continue;
316 }
317 }
318
319 if ((ret = unlink(dent->d_name)) == -1) {
320 opkg_perror(ERROR, "Failed to unlink %s", dent->d_name);
321 break;
322 }
323 }
324
325 if (chdir("..") == -1) {
326 ret = -1;
327 opkg_perror(ERROR, "Failed to change to dir %s/..", path);
328 }
329
330 if (rmdir(path) == -1) {
331 ret = -1;
332 opkg_perror(ERROR, "Failed to remove dir %s", path);
333 }
334
335 if (closedir(dir) == -1) {
336 ret = -1;
337 opkg_perror(ERROR, "Failed to close dir %s", path);
338 }
339
340 return ret;
341 }
342
343 static int urlencode_is_specialchar(char c)
344 {
345 switch (c) {
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 case '=':
363 case '%':
364 return 1;
365
366 default:
367 return 0;
368 }
369 }
370
371 char *urlencode_path(const char *filename)
372 {
373 size_t len = 0;
374 const unsigned char *in;
375 unsigned char *copy, *out;
376
377 for (in = (unsigned char *)filename; *in != 0; in++)
378 len += urlencode_is_specialchar(*in) ? 3 : 1;
379
380 copy = xcalloc(1, len + 1);
381
382 for (in = (unsigned char *)filename, out = copy; *in != 0; in++) {
383 if (urlencode_is_specialchar(*in)) {
384 *out++ = '%';
385 *out++ = bin2hex[*in / 16];
386 *out++ = bin2hex[*in % 16];
387 }
388 else {
389 *out++ = *in;
390 }
391 }
392
393 return (char *)copy;
394 }
395
396 char *urldecode_path(const char *filename)
397 {
398 unsigned char *copy = (unsigned char *)xstrdup(filename);
399 unsigned char *in, *out;
400
401 for (in = copy, out = copy; *in != 0; in++) {
402 if (*in == '%' && isxdigit(in[1]) && isxdigit(in[2])) {
403 *out++ = hex2bin(in[1]) * 16 + hex2bin(in[2]);
404 in += 2;
405 }
406 else {
407 *out++ = *in;
408 }
409 }
410
411 *out = 0;
412
413 return (char *)copy;
414 }