• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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  */
get_file_hash(HashAlgorithm algorithm,const char * path,char * output_string,size_t max_output_string)39 int 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 
117 struct list {
118     char *name;
119     struct list *next;
120 };
121 
cmp(const void * a,const void * b)122 static 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 
recurse(HashAlgorithm algorithm,const char * directory_path,struct list ** out)129 static 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);
162             closedir(d);
163             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  */
get_recursive_hash_manifest(HashAlgorithm algorithm,const char * directory_path,char ** output_string)265 int 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 }
336