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