Merge commit 'grg' into HEAD
[oweals/opkg-lede.git] / libopkg / file_util.c
1 /* file_util.c - convenience routines for common stat operations
2
3    Carl D. Worth
4
5    Copyright (C) 2001 University of Southern California
6
7    This program is free software; you can redistribute it and/or
8    modify it under the terms of the GNU General Public License as
9    published by the Free Software Foundation; either version 2, or (at
10    your option) any later version.
11
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16 */
17
18 #include "includes.h"
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <dirent.h>
22
23 #include "sprintf_alloc.h"
24 #include "file_util.h"
25 #include "md5.h"
26 #include "libbb/libbb.h"
27 #undef strlen
28
29 #if defined HAVE_SHA256
30 #include "sha256.h"
31 #endif
32
33 int
34 file_exists(const char *file_name)
35 {
36         struct stat st;
37
38         if (stat(file_name, &st) == -1)
39                 return 0;
40
41         return 1;
42 }
43
44 int
45 file_is_dir(const char *file_name)
46 {
47         struct stat st;
48
49         if (stat(file_name, &st) == -1)
50                 return 0;
51
52         return S_ISDIR(st.st_mode);
53 }
54
55 /* read a single line from a file, stopping at a newline or EOF.
56    If a newline is read, it will appear in the resulting string.
57    Return value is a malloc'ed char * which should be freed at
58    some point by the caller.
59
60    Return value is NULL if the file is at EOF when called.
61 */
62 char *
63 file_read_line_alloc(FILE *fp)
64 {
65         char buf[BUFSIZ];
66         unsigned int buf_len;
67         char *line = NULL;
68         unsigned int line_size = 0;
69         int got_nl = 0;
70
71         buf[0] = '\0';
72
73         while (fgets(buf, BUFSIZ, fp)) {
74                 buf_len = strlen(buf);
75                 if (buf[buf_len - 1] == '\n') {
76                         buf_len--;
77                         buf[buf_len] = '\0';
78                         got_nl = 1;
79                 }
80                 if (line) {
81                         line_size += buf_len;
82                         line = xrealloc(line, line_size+1);
83                         strncat(line, buf, line_size);
84                 } else {
85                         line_size = buf_len + 1;
86                         line = xstrdup(buf);
87                 }
88                 if (got_nl)
89                         break;
90         }
91
92         return line;
93 }
94
95 int
96 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
117 file_copy(const char *src, const char *dest)
118 {
119         int err;
120
121         err = copy_file(src, dest, FILEUTILS_FORCE | FILEUTILS_PRESERVE_STATUS);
122         if (err)
123                 opkg_msg(ERROR, "Failed to copy file %s to %s.\n",
124                                 src, dest);
125
126         return err;
127 }
128
129 int
130 file_mkdir_hier(const char *path, long mode)
131 {
132         return make_directory(path, mode, FILEUTILS_RECUR);
133 }
134
135 char *file_md5sum_alloc(const char *file_name)
136 {
137     static const int md5sum_bin_len = 16;
138     static const int md5sum_hex_len = 32;
139
140     static const unsigned char bin2hex[16] = {
141         '0', '1', '2', '3',
142         '4', '5', '6', '7',
143         '8', '9', 'a', 'b',
144         'c', 'd', 'e', 'f'
145     };
146
147     int i, err;
148     FILE *file;
149     char *md5sum_hex;
150     unsigned char md5sum_bin[md5sum_bin_len];
151
152     md5sum_hex = xcalloc(1, md5sum_hex_len + 1);
153
154     file = fopen(file_name, "r");
155     if (file == NULL) {
156         opkg_perror(ERROR, "Failed to open file %s", file_name);
157         free(md5sum_hex);
158         return NULL;
159     }
160
161     err = md5_stream(file, md5sum_bin);
162     if (err) {
163         opkg_msg(ERROR, "Could't compute md5sum for %s.\n", file_name);
164         fclose(file);
165         free(md5sum_hex);
166         return NULL;
167     }
168
169     fclose(file);
170
171     for (i=0; i < md5sum_bin_len; i++) {
172         md5sum_hex[i*2] = bin2hex[md5sum_bin[i] >> 4];
173         md5sum_hex[i*2+1] = bin2hex[md5sum_bin[i] & 0xf];
174     }
175     
176     md5sum_hex[md5sum_hex_len] = '\0';
177     
178     return md5sum_hex;
179 }
180
181 #ifdef HAVE_SHA256
182 char *file_sha256sum_alloc(const char *file_name)
183 {
184     static const int sha256sum_bin_len = 32;
185     static const int sha256sum_hex_len = 64;
186
187     static const unsigned char bin2hex[16] = {
188         '0', '1', '2', '3',
189         '4', '5', '6', '7',
190         '8', '9', 'a', 'b',
191         'c', 'd', 'e', 'f'
192     };
193
194     int i, err;
195     FILE *file;
196     char *sha256sum_hex;
197     unsigned char sha256sum_bin[sha256sum_bin_len];
198
199     sha256sum_hex = xcalloc(1, sha256sum_hex_len + 1);
200
201     file = fopen(file_name, "r");
202     if (file == NULL) {
203         opkg_perror(ERROR, "Failed to open file %s", file_name);
204         free(sha256sum_hex);
205         return NULL;
206     }
207
208     err = sha256_stream(file, sha256sum_bin);
209     if (err) {
210         opkg_msg(ERROR, "Could't compute sha256sum for %s.\n", 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
231 int
232 rm_r(const char *path)
233 {
234         int ret = 0;
235         DIR *dir;
236         struct dirent *dent;
237
238         dir = opendir(path);
239         if (dir == NULL) {
240                 opkg_perror(ERROR, "Failed to open dir %s", path);
241                 return -1;
242         }
243
244         if (fchdir(dirfd(dir)) == -1) {
245                 opkg_perror(ERROR, "Failed to change to dir %s", path);
246                 closedir(dir);
247                 return -1;
248         }
249
250         while (1) {
251                 errno = 0;
252                 if ((dent = readdir(dir)) == NULL) {
253                         if (errno) {
254                                 opkg_perror(ERROR, "Failed to read dir %s",
255                                                 path);
256                                 ret = -1;
257                         }
258                         break;
259                 }
260
261                 if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
262                         continue;
263
264 #ifdef _BSD_SOURCE
265                 if (dent->d_type == DT_DIR) {
266                         if ((ret = rm_r(dent->d_name)) == -1)
267                                 break;
268                         continue;
269                 } else if (dent->d_type == DT_UNKNOWN)
270 #endif
271                 {
272                         struct stat st;
273                         if ((ret = lstat(dent->d_name, &st)) == -1) {
274                                 opkg_perror(ERROR, "Failed to lstat %s",
275                                                 dent->d_name);
276                                 break;
277                         }
278                         if (S_ISDIR(st.st_mode)) {
279                                 if ((ret = rm_r(dent->d_name)) == -1)
280                                         break;
281                                 continue;
282                         }
283                 }
284
285                 if ((ret = unlink(dent->d_name)) == -1) {
286                         opkg_perror(ERROR, "Failed to unlink %s", dent->d_name);
287                         break;
288                 }
289         }
290
291         if (chdir("..") == -1) {
292                 ret = -1;
293                 opkg_perror(ERROR, "Failed to change to dir %s/..", path);
294         }
295
296         if (rmdir(path) == -1 ) {
297                 ret = -1;
298                 opkg_perror(ERROR, "Failed to remove dir %s", path);
299         }
300
301         if (closedir(dir) == -1) {
302                 ret = -1;
303                 opkg_perror(ERROR, "Failed to close dir %s", path);
304         }
305
306         return ret;
307 }