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 */ 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 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 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 */ 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