1a98df6a102bc0a9d9495b57328a16a9cac02b85
[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 #ifdef HAVE_MD5
29 #include "md5.h"
30 #endif
31 #include "libbb/libbb.h"
32
33 #if defined HAVE_SHA256
34 #include "sha256.h"
35 #endif
36
37 int file_exists(const char *file_name)
38 {
39         struct stat st;
40
41         if (stat(file_name, &st) == -1)
42                 return 0;
43
44         return 1;
45 }
46
47 int file_is_dir(const char *file_name)
48 {
49         struct stat st;
50
51         if (stat(file_name, &st) == -1)
52                 return 0;
53
54         return S_ISDIR(st.st_mode);
55 }
56
57 /* read a single line from a file, stopping at a newline or EOF.
58    If a newline is read, it will appear in the resulting string.
59    Return value is a malloc'ed char * which should be freed at
60    some point by the caller.
61
62    Return value is NULL if the file is at EOF when called.
63 */
64 char *file_read_line_alloc(FILE * fp)
65 {
66         char buf[BUFSIZ];
67         unsigned int buf_len;
68         char *line = NULL;
69         unsigned int line_size = 0;
70         int got_nl = 0;
71
72         buf[0] = '\0';
73
74         while (fgets(buf, BUFSIZ, fp)) {
75                 buf_len = strlen(buf);
76                 if (buf[buf_len - 1] == '\n') {
77                         buf_len--;
78                         buf[buf_len] = '\0';
79                         got_nl = 1;
80                 }
81                 if (line) {
82                         line_size += buf_len;
83                         line = xrealloc(line, line_size + 1);
84                         strncat(line, buf, line_size);
85                 } else {
86                         line_size = buf_len + 1;
87                         line = xstrdup(buf);
88                 }
89                 if (got_nl)
90                         break;
91         }
92
93         return line;
94 }
95
96 int file_move(const char *src, const char *dest)
97 {
98         int err;
99
100         err = rename(src, dest);
101         if (err == -1) {
102                 if (errno == EXDEV) {
103                         /* src & dest live on different file systems */
104                         err = file_copy(src, dest);
105                         if (err == 0)
106                                 unlink(src);
107                 } else {
108                         opkg_perror(ERROR, "Failed to rename %s to %s",
109                                     src, dest);
110                 }
111         }
112
113         return err;
114 }
115
116 int file_copy(const char *src, const char *dest)
117 {
118         int err;
119
120         err = copy_file(src, dest, FILEUTILS_FORCE | FILEUTILS_PRESERVE_STATUS);
121         if (err)
122                 opkg_msg(ERROR, "Failed to copy file %s to %s.\n", src, dest);
123
124         return err;
125 }
126
127 int file_mkdir_hier(const char *path, long mode)
128 {
129         return make_directory(path, mode, FILEUTILS_RECUR);
130 }
131
132 #ifdef HAVE_MD5
133 char *file_md5sum_alloc(const char *file_name)
134 {
135         static const int md5sum_bin_len = 16;
136         static const int md5sum_hex_len = 32;
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         int i, err;
146         FILE *file;
147         char *md5sum_hex;
148         unsigned char md5sum_bin[md5sum_bin_len];
149
150         md5sum_hex = xcalloc(1, md5sum_hex_len + 1);
151
152         file = fopen(file_name, "r");
153         if (file == NULL) {
154                 opkg_perror(ERROR, "Failed to open file %s", file_name);
155                 free(md5sum_hex);
156                 return NULL;
157         }
158
159         err = md5_stream(file, md5sum_bin);
160         if (err) {
161                 opkg_msg(ERROR, "Could't compute md5sum for %s.\n", file_name);
162                 fclose(file);
163                 free(md5sum_hex);
164                 return NULL;
165         }
166
167         fclose(file);
168
169         for (i = 0; i < md5sum_bin_len; i++) {
170                 md5sum_hex[i * 2] = bin2hex[md5sum_bin[i] >> 4];
171                 md5sum_hex[i * 2 + 1] = bin2hex[md5sum_bin[i] & 0xf];
172         }
173
174         md5sum_hex[md5sum_hex_len] = '\0';
175
176         return md5sum_hex;
177 }
178 #endif
179
180 #ifdef HAVE_SHA256
181 char *file_sha256sum_alloc(const char *file_name)
182 {
183         static const int sha256sum_bin_len = 32;
184         static const int sha256sum_hex_len = 64;
185
186         static const unsigned char bin2hex[16] = {
187                 '0', '1', '2', '3',
188                 '4', '5', '6', '7',
189                 '8', '9', 'a', 'b',
190                 'c', 'd', 'e', 'f'
191         };
192
193         int i, err;
194         FILE *file;
195         char *sha256sum_hex;
196         unsigned char sha256sum_bin[sha256sum_bin_len];
197
198         sha256sum_hex = xcalloc(1, sha256sum_hex_len + 1);
199
200         file = fopen(file_name, "r");
201         if (file == NULL) {
202                 opkg_perror(ERROR, "Failed to open file %s", file_name);
203                 free(sha256sum_hex);
204                 return NULL;
205         }
206
207         err = sha256_stream(file, sha256sum_bin);
208         if (err) {
209                 opkg_msg(ERROR, "Could't compute sha256sum for %s.\n",
210                          file_name);
211                 fclose(file);
212                 free(sha256sum_hex);
213                 return NULL;
214         }
215
216         fclose(file);
217
218         for (i = 0; i < sha256sum_bin_len; i++) {
219                 sha256sum_hex[i * 2] = bin2hex[sha256sum_bin[i] >> 4];
220                 sha256sum_hex[i * 2 + 1] = bin2hex[sha256sum_bin[i] & 0xf];
221         }
222
223         sha256sum_hex[sha256sum_hex_len] = '\0';
224
225         return sha256sum_hex;
226 }
227
228 #endif
229
230 char *checksum_bin2hex(const char *src, size_t len)
231 {
232         unsigned char *p;
233         static unsigned char buf[65];
234         const unsigned char *s = (unsigned char *)src;
235         static const unsigned char bin2hex[16] = {
236                 '0', '1', '2', '3',
237                 '4', '5', '6', '7',
238                 '8', '9', 'a', 'b',
239                 'c', 'd', 'e', 'f'
240         };
241
242         if (!s || len > 32)
243                 return NULL;
244
245         for (p = buf; len > 0; s++, len--) {
246                 *p++ = bin2hex[*s / 16];
247                 *p++ = bin2hex[*s % 16];
248         }
249
250         *p = 0;
251
252         return (char *)buf;
253 }
254
255 char *checksum_hex2bin(const char *src, size_t *len)
256 {
257         size_t slen;
258         unsigned char *p;
259         const unsigned char *s = (unsigned char *)src;
260         static unsigned char buf[32];
261
262         if (!src) {
263                 *len = 0;
264                 return NULL;
265         }
266
267         while (isspace(*src))
268                 src++;
269
270         slen = strlen(src);
271
272         if (slen > 64) {
273                 *len = 0;
274                 return NULL;
275         }
276
277 #define hex(c) \
278         (c >= 'a' ? (c - 'a') : (c >= 'A' ? (c - 'A') : (c - '0')))
279
280         for (p = buf, *len = 0;
281              slen > 0 && isxdigit(s[0]) && isxdigit(s[1]);
282              slen--, s += 2, (*len)++)
283                 *p++ = hex(s[0]) * 16 + hex(s[1]);
284
285         return (char *)buf;
286 }
287
288 int rm_r(const char *path)
289 {
290         int ret = 0;
291         DIR *dir;
292         struct dirent *dent;
293
294         if (path == NULL) {
295                 opkg_perror(ERROR, "Missing directory parameter");
296                 return -1;
297         }
298
299         dir = opendir(path);
300         if (dir == NULL) {
301                 opkg_perror(ERROR, "Failed to open dir %s", path);
302                 return -1;
303         }
304
305         if (fchdir(dirfd(dir)) == -1) {
306                 opkg_perror(ERROR, "Failed to change to dir %s", path);
307                 closedir(dir);
308                 return -1;
309         }
310
311         while (1) {
312                 errno = 0;
313                 if ((dent = readdir(dir)) == NULL) {
314                         if (errno) {
315                                 opkg_perror(ERROR, "Failed to read dir %s",
316                                             path);
317                                 ret = -1;
318                         }
319                         break;
320                 }
321
322                 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
323                         continue;
324
325 #ifdef _BSD_SOURCE
326                 if (dent->d_type == DT_DIR) {
327                         if ((ret = rm_r(dent->d_name)) == -1)
328                                 break;
329                         continue;
330                 } else if (dent->d_type == DT_UNKNOWN)
331 #endif
332                 {
333                         struct stat st;
334                         if ((ret = lstat(dent->d_name, &st)) == -1) {
335                                 opkg_perror(ERROR, "Failed to lstat %s",
336                                             dent->d_name);
337                                 break;
338                         }
339                         if (S_ISDIR(st.st_mode)) {
340                                 if ((ret = rm_r(dent->d_name)) == -1)
341                                         break;
342                                 continue;
343                         }
344                 }
345
346                 if ((ret = unlink(dent->d_name)) == -1) {
347                         opkg_perror(ERROR, "Failed to unlink %s", dent->d_name);
348                         break;
349                 }
350         }
351
352         if (chdir("..") == -1) {
353                 ret = -1;
354                 opkg_perror(ERROR, "Failed to change to dir %s/..", path);
355         }
356
357         if (rmdir(path) == -1) {
358                 ret = -1;
359                 opkg_perror(ERROR, "Failed to remove dir %s", path);
360         }
361
362         if (closedir(dir) == -1) {
363                 ret = -1;
364                 opkg_perror(ERROR, "Failed to close dir %s", path);
365         }
366
367         return ret;
368 }