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