blob: 098b5db5a224d5394f9001217c5bf6ed47d72f3d [file] [log] [blame]
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <dirent.h>
18#include <errno.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <sha1.h>
23#include <unistd.h>
24#include <limits.h>
25
26#include <sys/stat.h>
27
28#include <netinet/in.h>
29#include <resolv.h>
30
31#include <cutils/dir_hash.h>
32
33/**
34 * Copies, if it fits within max_output_string bytes, into output_string
35 * a hash of the contents, size, permissions, uid, and gid of the file
36 * specified by path, using the specified algorithm. Returns the length
37 * of the output string, or a negative number if the buffer is too short.
38 */
39int get_file_hash(HashAlgorithm algorithm, const char *path,
40 char *output_string, size_t max_output_string) {
41 SHA1_CTX context;
42 struct stat sb;
43 unsigned char md[SHA1_DIGEST_LENGTH];
44 int used;
45 size_t n;
46
47 if (algorithm != SHA_1) {
48 errno = EINVAL;
49 return -1;
50 }
51
52 if (stat(path, &sb) != 0) {
53 return -1;
54 }
55
56 if (S_ISLNK(sb.st_mode)) {
57 char buf[PATH_MAX];
58 int len;
59
60 len = readlink(path, buf, sizeof(buf));
61 if (len < 0) {
62 return -1;
63 }
64
65 SHA1Init(&context);
66 SHA1Update(&context, (unsigned char *) buf, len);
67 SHA1Final(md, &context);
68 } else if (S_ISREG(sb.st_mode)) {
69 char buf[10000];
70 FILE *f = fopen(path, "rb");
71 int len;
72
73 if (f == NULL) {
74 return -1;
75 }
76
77 SHA1Init(&context);
78
79 while ((len = fread(buf, 1, sizeof(buf), f)) > 0) {
80 SHA1Update(&context, (unsigned char *) buf, len);
81 }
82
83 if (ferror(f)) {
84 fclose(f);
85 return -1;
86 }
87
88 fclose(f);
89 SHA1Final(md, &context);
90 }
91
92 if (S_ISLNK(sb.st_mode) || S_ISREG(sb.st_mode)) {
93 used = b64_ntop(md, SHA1_DIGEST_LENGTH,
94 output_string, max_output_string);
95 if (used < 0) {
96 errno = ENOSPC;
97 return -1;
98 }
99
100 n = snprintf(output_string + used, max_output_string - used,
101 " %d 0%o %d %d", (int) sb.st_size, sb.st_mode,
102 (int) sb.st_uid, (int) sb.st_gid);
103 } else {
104 n = snprintf(output_string, max_output_string,
105 "- - 0%o %d %d", sb.st_mode,
106 (int) sb.st_uid, (int) sb.st_gid);
107 }
108
109 if (n >= max_output_string - used) {
110 errno = ENOSPC;
111 return -(used + n);
112 }
113
114 return used + n;
115}
116
117struct list {
118 char *name;
119 struct list *next;
120};
121
122static int cmp(const void *a, const void *b) {
123 struct list *const *ra = a;
124 struct list *const *rb = b;
125
126 return strcmp((*ra)->name, (*rb)->name);
127}
128
129static int recurse(HashAlgorithm algorithm, const char *directory_path,
130 struct list **out) {
131 struct list *list = NULL;
132 struct list *f;
133
134 struct dirent *de;
135 DIR *d = opendir(directory_path);
136
137 if (d == NULL) {
138 return -1;
139 }
140
141 while ((de = readdir(d)) != NULL) {
142 if (strcmp(de->d_name, ".") == 0) {
143 continue;
144 }
145 if (strcmp(de->d_name, "..") == 0) {
146 continue;
147 }
148
149 char *name = malloc(strlen(de->d_name) + 1);
150 struct list *node = malloc(sizeof(struct list));
151
152 if (name == NULL || node == NULL) {
153 struct list *next;
154 for (f = list; f != NULL; f = next) {
155 next = f->next;
156 free(f->name);
157 free(f);
158 }
159
160 free(name);
161 free(node);
Elliott Hughes14e28d32013-10-29 14:12:46 -0700162 closedir(d);
The Android Open Source Projectdd7bc332009-03-03 19:32:55 -0800163 return -1;
164 }
165
166 strcpy(name, de->d_name);
167
168 node->name = name;
169 node->next = list;
170 list = node;
171 }
172
173 closedir(d);
174
175 for (f = list; f != NULL; f = f->next) {
176 struct stat sb;
177 char *name;
178 char outstr[NAME_MAX + 100];
179 char *keep;
180 struct list *res;
181
182 name = malloc(strlen(f->name) + strlen(directory_path) + 2);
183 if (name == NULL) {
184 struct list *next;
185 for (f = list; f != NULL; f = f->next) {
186 next = f->next;
187 free(f->name);
188 free(f);
189 }
190 for (f = *out; f != NULL; f = f->next) {
191 next = f->next;
192 free(f->name);
193 free(f);
194 }
195 *out = NULL;
196 return -1;
197 }
198
199 sprintf(name, "%s/%s", directory_path, f->name);
200
201 int len = get_file_hash(algorithm, name,
202 outstr, sizeof(outstr));
203 if (len < 0) {
204 // should not happen
205 return -1;
206 }
207
208 keep = malloc(len + strlen(name) + 3);
209 res = malloc(sizeof(struct list));
210
211 if (keep == NULL || res == NULL) {
212 struct list *next;
213 for (f = list; f != NULL; f = f->next) {
214 next = f->next;
215 free(f->name);
216 free(f);
217 }
218 for (f = *out; f != NULL; f = f->next) {
219 next = f->next;
220 free(f->name);
221 free(f);
222 }
223 *out = NULL;
224
225 free(keep);
226 free(res);
227 return -1;
228 }
229
230 sprintf(keep, "%s %s\n", name, outstr);
231
232 res->name = keep;
233 res->next = *out;
234 *out = res;
235
236 if ((stat(name, &sb) == 0) && S_ISDIR(sb.st_mode)) {
237 if (recurse(algorithm, name, out) < 0) {
238 struct list *next;
239 for (f = list; f != NULL; f = next) {
240 next = f->next;
241 free(f->name);
242 free(f);
243 }
244
245 return -1;
246 }
247 }
248 }
249
250 struct list *next;
251 for (f = list; f != NULL; f = next) {
252 next = f->next;
253
254 free(f->name);
255 free(f);
256 }
257}
258
259/**
260 * Allocates a string containing the names and hashes of all files recursively
261 * reached under the specified directory_path, using the specified algorithm.
262 * The string is returned as *output_string; the return value is the length
263 * of the string, or a negative number if there was a failure.
264 */
265int get_recursive_hash_manifest(HashAlgorithm algorithm,
266 const char *directory_path,
267 char **output_string) {
268 struct list *out = NULL;
269 struct list *r;
270 struct list **list;
271 int count = 0;
272 int len = 0;
273 int retlen = 0;
274 int i;
275 char *buf;
276
277 if (recurse(algorithm, directory_path, &out) < 0) {
278 return -1;
279 }
280
281 for (r = out; r != NULL; r = r->next) {
282 count++;
283 len += strlen(r->name);
284 }
285
286 list = malloc(count * sizeof(struct list *));
287 if (list == NULL) {
288 struct list *next;
289 for (r = out; r != NULL; r = next) {
290 next = r->next;
291 free(r->name);
292 free(r);
293 }
294 return -1;
295 }
296
297 count = 0;
298 for (r = out; r != NULL; r = r->next) {
299 list[count++] = r;
300 }
301
302 qsort(list, count, sizeof(struct list *), cmp);
303
304 buf = malloc(len + 1);
305 if (buf == NULL) {
306 struct list *next;
307 for (r = out; r != NULL; r = next) {
308 next = r->next;
309 free(r->name);
310 free(r);
311 }
312 free(list);
313 return -1;
314 }
315
316 for (i = 0; i < count; i++) {
317 int n = strlen(list[i]->name);
318
319 strcpy(buf + retlen, list[i]->name);
320 retlen += n;
321 }
322
323 free(list);
324
325 struct list *next;
326 for (r = out; r != NULL; r = next) {
327 next = r->next;
328
329 free(r->name);
330 free(r);
331 }
332
333 *output_string = buf;
334 return retlen;
335}