• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 // This program takes a file on an ext4 filesystem and produces a list
18 // of the blocks that file occupies, which enables the file contents
19 // to be read directly from the block device without mounting the
20 // filesystem.
21 //
22 // If the filesystem is using an encrypted block device, it will also
23 // read the file and rewrite it to the same blocks of the underlying
24 // (unencrypted) block device, so the file contents can be read
25 // without the need for the decryption key.
26 //
27 // The output of this program is a "block map" which looks like this:
28 //
29 //     /dev/block/platform/msm_sdcc.1/by-name/userdata     # block device
30 //     49652 4096                        # file size in bytes, block size
31 //     3                                 # count of block ranges
32 //     1000 1008                         # block range 0
33 //     2100 2102                         # ... block range 1
34 //     30 33                             # ... block range 2
35 //
36 // Each block range represents a half-open interval; the line "30 33"
37 // reprents the blocks [30, 31, 32].
38 //
39 // Recovery can take this block map file and retrieve the underlying
40 // file data to use as an update package.
41 
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <stdarg.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <fcntl.h>
48 #include <linux/fs.h>
49 #include <sys/mman.h>
50 
51 #define LOG_TAG "uncrypt"
52 #include <log/log.h>
53 #include <cutils/properties.h>
54 #include <fs_mgr.h>
55 
56 #define WINDOW_SIZE 5
57 #define RECOVERY_COMMAND_FILE "/cache/recovery/command"
58 #define RECOVERY_COMMAND_FILE_TMP "/cache/recovery/command.tmp"
59 #define CACHE_BLOCK_MAP "/cache/recovery/block.map"
60 
61 static struct fstab* fstab = NULL;
62 
write_at_offset(unsigned char * buffer,size_t size,int wfd,off64_t offset)63 static int write_at_offset(unsigned char* buffer, size_t size,
64                            int wfd, off64_t offset)
65 {
66     lseek64(wfd, offset, SEEK_SET);
67     size_t written = 0;
68     while (written < size) {
69         ssize_t wrote = write(wfd, buffer + written, size - written);
70         if (wrote < 0) {
71             ALOGE("error writing offset %lld: %s\n", offset, strerror(errno));
72             return -1;
73         }
74         written += wrote;
75     }
76     return 0;
77 }
78 
add_block_to_ranges(int ** ranges,int * range_alloc,int * range_used,int new_block)79 void add_block_to_ranges(int** ranges, int* range_alloc, int* range_used, int new_block)
80 {
81     // If the current block start is < 0, set the start to the new
82     // block.  (This only happens for the very first block of the very
83     // first range.)
84     if ((*ranges)[*range_used*2-2] < 0) {
85         (*ranges)[*range_used*2-2] = new_block;
86         (*ranges)[*range_used*2-1] = new_block;
87     }
88 
89     if (new_block == (*ranges)[*range_used*2-1]) {
90         // If the new block comes immediately after the current range,
91         // all we have to do is extend the current range.
92         ++(*ranges)[*range_used*2-1];
93     } else {
94         // We need to start a new range.
95 
96         // If there isn't enough room in the array, we need to expand it.
97         if (*range_used >= *range_alloc) {
98             *range_alloc *= 2;
99             *ranges = realloc(*ranges, *range_alloc * 2 * sizeof(int));
100         }
101 
102         ++*range_used;
103         (*ranges)[*range_used*2-2] = new_block;
104         (*ranges)[*range_used*2-1] = new_block+1;
105     }
106 }
107 
read_fstab()108 static struct fstab* read_fstab()
109 {
110     fstab = NULL;
111 
112     // The fstab path is always "/fstab.${ro.hardware}".
113     char fstab_path[PATH_MAX+1] = "/fstab.";
114     if (!property_get("ro.hardware", fstab_path+strlen(fstab_path), "")) {
115         ALOGE("failed to get ro.hardware\n");
116         return NULL;
117     }
118 
119     fstab = fs_mgr_read_fstab(fstab_path);
120     if (!fstab) {
121         ALOGE("failed to read %s\n", fstab_path);
122         return NULL;
123     }
124 
125     return fstab;
126 }
127 
find_block_device(const char * path,int * encryptable,int * encrypted)128 const char* find_block_device(const char* path, int* encryptable, int* encrypted)
129 {
130     // Look for a volume whose mount point is the prefix of path and
131     // return its block device.  Set encrypted if it's currently
132     // encrypted.
133     int i;
134     for (i = 0; i < fstab->num_entries; ++i) {
135         struct fstab_rec* v = &fstab->recs[i];
136         if (!v->mount_point) continue;
137         int len = strlen(v->mount_point);
138         if (strncmp(path, v->mount_point, len) == 0 &&
139             (path[len] == '/' || path[len] == 0)) {
140             *encrypted = 0;
141             *encryptable = 0;
142             if (fs_mgr_is_encryptable(v)) {
143                 *encryptable = 1;
144                 char buffer[PROPERTY_VALUE_MAX+1];
145                 if (property_get("ro.crypto.state", buffer, "") &&
146                     strcmp(buffer, "encrypted") == 0) {
147                     *encrypted = 1;
148                 }
149             }
150             return v->blk_device;
151         }
152     }
153 
154     return NULL;
155 }
156 
parse_recovery_command_file()157 char* parse_recovery_command_file()
158 {
159     char* fn = NULL;
160     int count = 0;
161     char temp[1024];
162 
163     FILE* f = fopen(RECOVERY_COMMAND_FILE, "r");
164     if (f == NULL) {
165         return NULL;
166     }
167     int fd = open(RECOVERY_COMMAND_FILE_TMP, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
168     if (fd < 0) {
169         ALOGE("failed to open %s\n", RECOVERY_COMMAND_FILE_TMP);
170         return NULL;
171     }
172     FILE* fo = fdopen(fd, "w");
173 
174     while (fgets(temp, sizeof(temp), f)) {
175         printf("read: %s", temp);
176         if (strncmp(temp, "--update_package=/data/", strlen("--update_package=/data/")) == 0) {
177             fn = strdup(temp + strlen("--update_package="));
178             strcpy(temp, "--update_package=@" CACHE_BLOCK_MAP "\n");
179         }
180         fputs(temp, fo);
181     }
182     fclose(f);
183     fsync(fd);
184     fclose(fo);
185 
186     if (fn) {
187         char* newline = strchr(fn, '\n');
188         if (newline) *newline = 0;
189     }
190     return fn;
191 }
192 
produce_block_map(const char * path,const char * map_file,const char * blk_dev,int encrypted)193 int produce_block_map(const char* path, const char* map_file, const char* blk_dev,
194                       int encrypted)
195 {
196     struct stat sb;
197     int ret;
198 
199     int mapfd = open(map_file, O_WRONLY | O_CREAT | O_SYNC, S_IRUSR | S_IWUSR);
200     if (mapfd < 0) {
201         ALOGE("failed to open %s\n", map_file);
202         return -1;
203     }
204     FILE* mapf = fdopen(mapfd, "w");
205 
206     ret = stat(path, &sb);
207     if (ret != 0) {
208         ALOGE("failed to stat %s\n", path);
209         return -1;
210     }
211 
212     ALOGI(" block size: %ld bytes\n", (long)sb.st_blksize);
213 
214     int blocks = ((sb.st_size-1) / sb.st_blksize) + 1;
215     ALOGI("  file size: %lld bytes, %d blocks\n", (long long)sb.st_size, blocks);
216 
217     int* ranges;
218     int range_alloc = 1;
219     int range_used = 1;
220     ranges = malloc(range_alloc * 2 * sizeof(int));
221     ranges[0] = -1;
222     ranges[1] = -1;
223 
224     fprintf(mapf, "%s\n%lld %lu\n", blk_dev, (long long)sb.st_size, (unsigned long)sb.st_blksize);
225 
226     unsigned char* buffers[WINDOW_SIZE];
227     int i;
228     if (encrypted) {
229         for (i = 0; i < WINDOW_SIZE; ++i) {
230             buffers[i] = malloc(sb.st_blksize);
231         }
232     }
233     int head_block = 0;
234     int head = 0, tail = 0;
235     size_t pos = 0;
236 
237     int fd = open(path, O_RDONLY);
238     if (fd < 0) {
239         ALOGE("failed to open fd for reading: %s\n", strerror(errno));
240         return -1;
241     }
242     fsync(fd);
243 
244     int wfd = -1;
245     if (encrypted) {
246         wfd = open(blk_dev, O_WRONLY | O_SYNC);
247         if (wfd < 0) {
248             ALOGE("failed to open fd for writing: %s\n", strerror(errno));
249             return -1;
250         }
251     }
252 
253     while (pos < sb.st_size) {
254         if ((tail+1) % WINDOW_SIZE == head) {
255             // write out head buffer
256             int block = head_block;
257             ret = ioctl(fd, FIBMAP, &block);
258             if (ret != 0) {
259                 ALOGE("failed to find block %d\n", head_block);
260                 return -1;
261             }
262             add_block_to_ranges(&ranges, &range_alloc, &range_used, block);
263             if (encrypted) {
264                 if (write_at_offset(buffers[head], sb.st_blksize, wfd, (off64_t)sb.st_blksize * block) != 0) {
265                     return -1;
266                 }
267             }
268             head = (head + 1) % WINDOW_SIZE;
269             ++head_block;
270         }
271 
272         // read next block to tail
273         if (encrypted) {
274             size_t so_far = 0;
275             while (so_far < sb.st_blksize && pos < sb.st_size) {
276                 ssize_t this_read = read(fd, buffers[tail] + so_far, sb.st_blksize - so_far);
277                 if (this_read < 0) {
278                     ALOGE("failed to read: %s\n", strerror(errno));
279                     return -1;
280                 }
281                 so_far += this_read;
282                 pos += this_read;
283             }
284         } else {
285             // If we're not encrypting; we don't need to actually read
286             // anything, just skip pos forward as if we'd read a
287             // block.
288             pos += sb.st_blksize;
289         }
290         tail = (tail+1) % WINDOW_SIZE;
291     }
292 
293     while (head != tail) {
294         // write out head buffer
295         int block = head_block;
296         ret = ioctl(fd, FIBMAP, &block);
297         if (ret != 0) {
298             ALOGE("failed to find block %d\n", head_block);
299             return -1;
300         }
301         add_block_to_ranges(&ranges, &range_alloc, &range_used, block);
302         if (encrypted) {
303             if (write_at_offset(buffers[head], sb.st_blksize, wfd, (off64_t)sb.st_blksize * block) != 0) {
304                 return -1;
305             }
306         }
307         head = (head + 1) % WINDOW_SIZE;
308         ++head_block;
309     }
310 
311     fprintf(mapf, "%d\n", range_used);
312     for (i = 0; i < range_used; ++i) {
313         fprintf(mapf, "%d %d\n", ranges[i*2], ranges[i*2+1]);
314     }
315 
316     fsync(mapfd);
317     fclose(mapf);
318     close(fd);
319     if (encrypted) {
320         fsync(wfd);
321         close(wfd);
322     }
323 
324     return 0;
325 }
326 
wipe_misc()327 void wipe_misc() {
328     ALOGI("removing old commands from misc");
329     int i;
330     for (i = 0; i < fstab->num_entries; ++i) {
331         struct fstab_rec* v = &fstab->recs[i];
332         if (!v->mount_point) continue;
333         if (strcmp(v->mount_point, "/misc") == 0) {
334             int fd = open(v->blk_device, O_WRONLY | O_SYNC);
335             uint8_t zeroes[1088];   // sizeof(bootloader_message) from recovery
336             memset(zeroes, 0, sizeof(zeroes));
337 
338             size_t written = 0;
339             size_t size = sizeof(zeroes);
340             while (written < size) {
341                 ssize_t w = write(fd, zeroes, size-written);
342                 if (w < 0 && errno != EINTR) {
343                     ALOGE("zero write failed: %s\n", strerror(errno));
344                     return;
345                 } else {
346                     written += w;
347                 }
348             }
349             fsync(fd);
350             close(fd);
351         }
352     }
353 }
354 
reboot_to_recovery()355 void reboot_to_recovery() {
356     ALOGI("rebooting to recovery");
357     property_set("sys.powerctl", "reboot,recovery");
358     sleep(10);
359     ALOGE("reboot didn't succeed?");
360 }
361 
main(int argc,char ** argv)362 int main(int argc, char** argv)
363 {
364     const char* input_path;
365     const char* map_file;
366     int do_reboot = 1;
367 
368     if (argc != 1 && argc != 3) {
369         fprintf(stderr, "usage: %s [<transform_path> <map_file>]\n", argv[0]);
370         return 2;
371     }
372 
373     if (argc == 3) {
374         // when command-line args are given this binary is being used
375         // for debugging; don't reboot to recovery at the end.
376         input_path = argv[1];
377         map_file = argv[2];
378         do_reboot = 0;
379     } else {
380         input_path = parse_recovery_command_file();
381         if (input_path == NULL) {
382             // if we're rebooting to recovery without a package (say,
383             // to wipe data), then we don't need to do anything before
384             // going to recovery.
385             ALOGI("no recovery command file or no update package arg");
386             reboot_to_recovery();
387             return 1;
388         }
389         map_file = CACHE_BLOCK_MAP;
390     }
391 
392     ALOGI("update package is %s", input_path);
393 
394     // Turn the name of the file we're supposed to convert into an
395     // absolute path, so we can find what filesystem it's on.
396     char path[PATH_MAX+1];
397     if (realpath(input_path, path) == NULL) {
398         ALOGE("failed to convert %s to absolute path: %s", input_path, strerror(errno));
399         return 1;
400     }
401 
402     int encryptable;
403     int encrypted;
404     if (read_fstab() == NULL) {
405         return 1;
406     }
407     const char* blk_dev = find_block_device(path, &encryptable, &encrypted);
408     if (blk_dev == NULL) {
409         ALOGE("failed to find block device for %s", path);
410         return 1;
411     }
412 
413     // If the filesystem it's on isn't encrypted, we only produce the
414     // block map, we don't rewrite the file contents (it would be
415     // pointless to do so).
416     ALOGI("encryptable: %s\n", encryptable ? "yes" : "no");
417     ALOGI("  encrypted: %s\n", encrypted ? "yes" : "no");
418 
419     // Recovery supports installing packages from 3 paths: /cache,
420     // /data, and /sdcard.  (On a particular device, other locations
421     // may work, but those are three we actually expect.)
422     //
423     // On /data we want to convert the file to a block map so that we
424     // can read the package without mounting the partition.  On /cache
425     // and /sdcard we leave the file alone.
426     if (strncmp(path, "/data/", 6) != 0) {
427         // path does not start with "/data/"; leave it alone.
428         unlink(RECOVERY_COMMAND_FILE_TMP);
429     } else {
430         ALOGI("writing block map %s", map_file);
431         if (produce_block_map(path, map_file, blk_dev, encrypted) != 0) {
432             return 1;
433         }
434     }
435 
436     wipe_misc();
437     rename(RECOVERY_COMMAND_FILE_TMP, RECOVERY_COMMAND_FILE);
438     if (do_reboot) reboot_to_recovery();
439     return 0;
440 }
441