dir_hash.c revision 14e28d39f7f094225c1ddae8fa43bd792c621a8f
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 */
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);
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 */
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}
336