diff options
Diffstat (limited to 'libcutils/dir_hash.c')
-rw-r--r-- | libcutils/dir_hash.c | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/libcutils/dir_hash.c b/libcutils/dir_hash.c new file mode 100644 index 0000000..be14af6 --- /dev/null +++ b/libcutils/dir_hash.c @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <dirent.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sha1.h> +#include <unistd.h> +#include <limits.h> + +#include <sys/stat.h> + +#include <netinet/in.h> +#include <resolv.h> + +#include <cutils/dir_hash.h> + +/** + * Copies, if it fits within max_output_string bytes, into output_string + * a hash of the contents, size, permissions, uid, and gid of the file + * specified by path, using the specified algorithm. Returns the length + * of the output string, or a negative number if the buffer is too short. + */ +int get_file_hash(HashAlgorithm algorithm, const char *path, + char *output_string, size_t max_output_string) { + SHA1_CTX context; + struct stat sb; + unsigned char md[SHA1_DIGEST_LENGTH]; + int used; + size_t n; + + if (algorithm != SHA_1) { + errno = EINVAL; + return -1; + } + + if (stat(path, &sb) != 0) { + return -1; + } + + if (S_ISLNK(sb.st_mode)) { + char buf[PATH_MAX]; + int len; + + len = readlink(path, buf, sizeof(buf)); + if (len < 0) { + return -1; + } + + SHA1Init(&context); + SHA1Update(&context, (unsigned char *) buf, len); + SHA1Final(md, &context); + } else if (S_ISREG(sb.st_mode)) { + char buf[10000]; + FILE *f = fopen(path, "rb"); + int len; + + if (f == NULL) { + return -1; + } + + SHA1Init(&context); + + while ((len = fread(buf, 1, sizeof(buf), f)) > 0) { + SHA1Update(&context, (unsigned char *) buf, len); + } + + if (ferror(f)) { + fclose(f); + return -1; + } + + fclose(f); + SHA1Final(md, &context); + } + + if (S_ISLNK(sb.st_mode) || S_ISREG(sb.st_mode)) { + used = b64_ntop(md, SHA1_DIGEST_LENGTH, + output_string, max_output_string); + if (used < 0) { + errno = ENOSPC; + return -1; + } + + n = snprintf(output_string + used, max_output_string - used, + " %d 0%o %d %d", (int) sb.st_size, sb.st_mode, + (int) sb.st_uid, (int) sb.st_gid); + } else { + n = snprintf(output_string, max_output_string, + "- - 0%o %d %d", sb.st_mode, + (int) sb.st_uid, (int) sb.st_gid); + } + + if (n >= max_output_string - used) { + errno = ENOSPC; + return -(used + n); + } + + return used + n; +} + +struct list { + char *name; + struct list *next; +}; + +static int cmp(const void *a, const void *b) { + struct list *const *ra = a; + struct list *const *rb = b; + + return strcmp((*ra)->name, (*rb)->name); +} + +static int recurse(HashAlgorithm algorithm, const char *directory_path, + struct list **out) { + struct list *list = NULL; + struct list *f; + + struct dirent *de; + DIR *d = opendir(directory_path); + + if (d == NULL) { + return -1; + } + + while ((de = readdir(d)) != NULL) { + if (strcmp(de->d_name, ".") == 0) { + continue; + } + if (strcmp(de->d_name, "..") == 0) { + continue; + } + + char *name = malloc(strlen(de->d_name) + 1); + struct list *node = malloc(sizeof(struct list)); + + if (name == NULL || node == NULL) { + struct list *next; + for (f = list; f != NULL; f = next) { + next = f->next; + free(f->name); + free(f); + } + + free(name); + free(node); + return -1; + } + + strcpy(name, de->d_name); + + node->name = name; + node->next = list; + list = node; + } + + closedir(d); + + for (f = list; f != NULL; f = f->next) { + struct stat sb; + char *name; + char outstr[NAME_MAX + 100]; + char *keep; + struct list *res; + + name = malloc(strlen(f->name) + strlen(directory_path) + 2); + if (name == NULL) { + struct list *next; + for (f = list; f != NULL; f = f->next) { + next = f->next; + free(f->name); + free(f); + } + for (f = *out; f != NULL; f = f->next) { + next = f->next; + free(f->name); + free(f); + } + *out = NULL; + return -1; + } + + sprintf(name, "%s/%s", directory_path, f->name); + + int len = get_file_hash(algorithm, name, + outstr, sizeof(outstr)); + if (len < 0) { + // should not happen + return -1; + } + + keep = malloc(len + strlen(name) + 3); + res = malloc(sizeof(struct list)); + + if (keep == NULL || res == NULL) { + struct list *next; + for (f = list; f != NULL; f = f->next) { + next = f->next; + free(f->name); + free(f); + } + for (f = *out; f != NULL; f = f->next) { + next = f->next; + free(f->name); + free(f); + } + *out = NULL; + + free(keep); + free(res); + return -1; + } + + sprintf(keep, "%s %s\n", name, outstr); + + res->name = keep; + res->next = *out; + *out = res; + + if ((stat(name, &sb) == 0) && S_ISDIR(sb.st_mode)) { + if (recurse(algorithm, name, out) < 0) { + struct list *next; + for (f = list; f != NULL; f = next) { + next = f->next; + free(f->name); + free(f); + } + + return -1; + } + } + } + + struct list *next; + for (f = list; f != NULL; f = next) { + next = f->next; + + free(f->name); + free(f); + } +} + +/** + * Allocates a string containing the names and hashes of all files recursively + * reached under the specified directory_path, using the specified algorithm. + * The string is returned as *output_string; the return value is the length + * of the string, or a negative number if there was a failure. + */ +int get_recursive_hash_manifest(HashAlgorithm algorithm, + const char *directory_path, + char **output_string) { + struct list *out = NULL; + struct list *r; + struct list **list; + int count = 0; + int len = 0; + int retlen = 0; + int i; + char *buf; + + if (recurse(algorithm, directory_path, &out) < 0) { + return -1; + } + + for (r = out; r != NULL; r = r->next) { + count++; + len += strlen(r->name); + } + + list = malloc(count * sizeof(struct list *)); + if (list == NULL) { + struct list *next; + for (r = out; r != NULL; r = next) { + next = r->next; + free(r->name); + free(r); + } + return -1; + } + + count = 0; + for (r = out; r != NULL; r = r->next) { + list[count++] = r; + } + + qsort(list, count, sizeof(struct list *), cmp); + + buf = malloc(len + 1); + if (buf == NULL) { + struct list *next; + for (r = out; r != NULL; r = next) { + next = r->next; + free(r->name); + free(r); + } + free(list); + return -1; + } + + for (i = 0; i < count; i++) { + int n = strlen(list[i]->name); + + strcpy(buf + retlen, list[i]->name); + retlen += n; + } + + free(list); + + struct list *next; + for (r = out; r != NULL; r = next) { + next = r->next; + + free(r->name); + free(r); + } + + *output_string = buf; + return retlen; +} |