Home | History | Annotate | Download | only in libcutils
      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