• 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             return -1;
163         }
164 
165         strcpy(name, de->d_name);
166 
167         node->name = name;
168         node->next = list;
169         list = node;
170     }
171 
172     closedir(d);
173 
174     for (f = list; f != NULL; f = f->next) {
175         struct stat sb;
176         char *name;
177         char outstr[NAME_MAX + 100];
178         char *keep;
179         struct list *res;
180 
181         name = malloc(strlen(f->name) + strlen(directory_path) + 2);
182         if (name == NULL) {
183             struct list *next;
184             for (f = list; f != NULL; f = f->next) {
185                 next = f->next;
186                 free(f->name);
187                 free(f);
188             }
189             for (f = *out; f != NULL; f = f->next) {
190                 next = f->next;
191                 free(f->name);
192                 free(f);
193             }
194             *out = NULL;
195             return -1;
196         }
197 
198         sprintf(name, "%s/%s", directory_path, f->name);
199 
200         int len = get_file_hash(algorithm, name,
201                                 outstr, sizeof(outstr));
202         if (len < 0) {
203             // should not happen
204             return -1;
205         }
206 
207         keep = malloc(len + strlen(name) + 3);
208         res = malloc(sizeof(struct list));
209 
210         if (keep == NULL || res == NULL) {
211             struct list *next;
212             for (f = list; f != NULL; f = f->next) {
213                 next = f->next;
214                 free(f->name);
215                 free(f);
216             }
217             for (f = *out; f != NULL; f = f->next) {
218                 next = f->next;
219                 free(f->name);
220                 free(f);
221             }
222             *out = NULL;
223 
224             free(keep);
225             free(res);
226             return -1;
227         }
228 
229         sprintf(keep, "%s %s\n", name, outstr);
230 
231         res->name = keep;
232         res->next = *out;
233         *out = res;
234 
235         if ((stat(name, &sb) == 0) && S_ISDIR(sb.st_mode)) {
236             if (recurse(algorithm, name, out) < 0) {
237                 struct list *next;
238                 for (f = list; f != NULL; f = next) {
239                     next = f->next;
240                     free(f->name);
241                     free(f);
242                 }
243 
244                 return -1;
245             }
246         }
247     }
248 
249     struct list *next;
250     for (f = list; f != NULL; f = next) {
251         next = f->next;
252 
253         free(f->name);
254         free(f);
255     }
256 }
257 
258 /**
259  * Allocates a string containing the names and hashes of all files recursively
260  * reached under the specified directory_path, using the specified algorithm.
261  * The string is returned as *output_string; the return value is the length
262  * of the string, or a negative number if there was a failure.
263  */
get_recursive_hash_manifest(HashAlgorithm algorithm,const char * directory_path,char ** output_string)264 int get_recursive_hash_manifest(HashAlgorithm algorithm,
265                                 const char *directory_path,
266                                 char **output_string) {
267     struct list *out = NULL;
268     struct list *r;
269     struct list **list;
270     int count = 0;
271     int len = 0;
272     int retlen = 0;
273     int i;
274     char *buf;
275 
276     if (recurse(algorithm, directory_path, &out) < 0) {
277         return -1;
278     }
279 
280     for (r = out; r != NULL; r = r->next) {
281         count++;
282         len += strlen(r->name);
283     }
284 
285     list = malloc(count * sizeof(struct list *));
286     if (list == NULL) {
287         struct list *next;
288         for (r = out; r != NULL; r = next) {
289             next = r->next;
290             free(r->name);
291             free(r);
292         }
293         return -1;
294     }
295 
296     count = 0;
297     for (r = out; r != NULL; r = r->next) {
298         list[count++] = r;
299     }
300 
301     qsort(list, count, sizeof(struct list *), cmp);
302 
303     buf = malloc(len + 1);
304     if (buf == NULL) {
305         struct list *next;
306         for (r = out; r != NULL; r = next) {
307             next = r->next;
308             free(r->name);
309             free(r);
310         }
311         free(list);
312         return -1;
313     }
314 
315     for (i = 0; i < count; i++) {
316         int n = strlen(list[i]->name);
317 
318         strcpy(buf + retlen, list[i]->name);
319         retlen += n;
320     }
321 
322     free(list);
323 
324     struct list *next;
325     for (r = out; r != NULL; r = next) {
326         next = r->next;
327 
328         free(r->name);
329         free(r);
330     }
331 
332     *output_string = buf;
333     return retlen;
334 }
335