libopkg: implement lightweight package listing logic
[oweals/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 }