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