blob: 1e1148bba960c299eea71765fffbf5e50e063047 [file] [log] [blame]
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -08001/*
2 * Copyright (C) 2008 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 "private/android_filesystem_config.h"
18
19#include <dirent.h>
20#include <limits.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <sys/types.h>
24#include <unistd.h>
25
26/*
27 * Recursively walk the directory tree at <sysdir>/<subdir>, writing
28 * script commands to set permissions and create symlinks.
29 * Assume the contents already have the specified default permissions,
30 * so only output commands if they need to be changed from the defaults.
31 *
32 * Note that permissions are set by fs_config(), which uses a lookup table of
33 * Android permissions. They are not drawn from the build host filesystem.
34 */
35static void walk_files(
36 const char *sysdir, const char *subdir,
37 unsigned default_uid, unsigned default_gid,
38 unsigned default_dir_mode, unsigned default_file_mode) {
39 const char *sep = strcmp(subdir, "") ? "/" : "";
40
41 char fn[PATH_MAX];
42 unsigned dir_uid = 0, dir_gid = 0, dir_mode = 0;
43 snprintf(fn, PATH_MAX, "system%s%s", sep, subdir);
44 fs_config(fn, 1, &dir_uid, &dir_gid, &dir_mode);
45
46 snprintf(fn, PATH_MAX, "%s%s%s", sysdir, sep, subdir);
47 DIR *dir = opendir(fn);
48 if (dir == NULL) {
49 perror(fn);
50 exit(1);
51 }
52
53 /*
54 * We can use "set_perm" and "set_perm_recursive" to set file permissions
55 * (owner, group, and file mode) for individual files and entire subtrees.
56 * We want to use set_perm_recursive efficiently to avoid setting the
57 * permissions of every single file in the system image individually.
58 *
59 * What we do is recursively set our entire subtree to the permissions
60 * used by the first file we encounter, and then use "set_perm" to adjust
61 * the permissions of subsequent files which don't match the first one.
62 * This is bad if the first file is an outlier, but it generally works.
63 * Subdirectories can do the same thing recursively if they're different.
64 */
65
66 int is_first = 1;
67 const struct dirent *e;
68 while ((e = readdir(dir))) {
69 // Skip over "." and ".." entries
70 if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, "..")) continue;
71
72 if (e->d_type == DT_LNK) { // Symlink
73
74 // Symlinks don't really have permissions, so this is orthogonal.
75 snprintf(fn, PATH_MAX, "%s/%s%s%s", sysdir, subdir, sep, e->d_name);
76 int len = readlink(fn, fn, PATH_MAX - 1);
77 if (len <= 0) {
78 perror(fn);
79 exit(1);
80 }
81 fn[len] = '\0';
82 printf("symlink %s SYSTEM:%s%s%s\n", fn, subdir, sep, e->d_name);
83
84 } else if (e->d_type == DT_DIR) { // Subdirectory
85
86 // Use the parent directory as the model for default permissions.
87 // We haven't seen a file, so just make up some file defaults.
88 if (is_first && (
89 dir_mode != default_dir_mode ||
90 dir_uid != default_uid || dir_gid != default_gid)) {
91 default_uid = dir_uid;
92 default_gid = dir_gid;
93 default_dir_mode = dir_mode;
94 default_file_mode = dir_mode & default_file_mode & 0666;
95 printf("set_perm_recursive %d %d 0%o 0%o SYSTEM:%s\n",
96 default_uid, default_gid,
97 default_dir_mode, default_file_mode,
98 subdir);
99 }
100
101 is_first = 0;
102
103 // Recursively handle the subdirectory.
104 // Note, the recursive call handles the directory's own permissions.
105 snprintf(fn, PATH_MAX, "%s%s%s", subdir, sep, e->d_name);
106 walk_files(sysdir, fn,
107 default_uid, default_gid,
108 default_dir_mode, default_file_mode);
109
110 } else { // Ordinary file
111
112 // Get the file's desired permissions.
113 unsigned file_uid = 0, file_gid = 0, file_mode = 0;
114 snprintf(fn, PATH_MAX, "system/%s%s%s", subdir, sep, e->d_name);
115 fs_config(fn, 0, &file_uid, &file_gid, &file_mode);
116
117 // If this is the first file, its mode gets to become the default.
118 if (is_first && (
119 dir_mode != default_dir_mode ||
120 file_mode != default_file_mode ||
121 dir_uid != default_uid || file_uid != default_uid ||
122 dir_gid != default_gid || file_gid != default_gid)) {
123 default_uid = dir_uid;
124 default_gid = dir_gid;
125 default_dir_mode = dir_mode;
126 default_file_mode = file_mode;
127 printf("set_perm_recursive %d %d 0%o 0%o SYSTEM:%s\n",
128 default_uid, default_gid,
129 default_dir_mode, default_file_mode,
130 subdir);
131 }
132
133 is_first = 0;
134
135 // Otherwise, override this file if it doesn't match the defaults.
136 if (file_mode != default_file_mode ||
137 file_uid != default_uid || file_gid != default_gid) {
138 printf("set_perm %d %d 0%o SYSTEM:%s%s%s\n",
139 file_uid, file_gid, file_mode,
140 subdir, sep, e->d_name);
141 }
142
143 }
144 }
145
146 // Set the directory's permissions directly, if they never got set.
147 if (dir_mode != default_dir_mode ||
148 dir_uid != default_uid || dir_gid != default_gid) {
149 printf("set_perm %d %d 0%o SYSTEM:%s\n",
150 dir_uid, dir_gid, dir_mode, subdir);
151 }
152
153 closedir(dir);
154}
155
156/*
157 * Generate the update script (in "Amend", see commands/recovery/commands.c)
158 * for the complete-reinstall OTA update packages the build system makes.
159 *
160 * The generated script makes a variety of sanity checks about the device,
161 * erases and reinstalls system files, and sets file permissions appropriately.
162 */
163int main(int argc, char *argv[]) {
164 if (argc != 3) {
165 fprintf(stderr, "usage: %s systemdir android-info.txt >update-script\n",
166 argv[0]);
167 return 2;
168 }
169
170 // ensure basic recovery script language compatibility
171 printf("assert compatible_with(\"0.2\") == \"true\"\n");
172
173 // if known, make sure the device name is correct
174 const char *device = getenv("TARGET_DEVICE");
175 if (device != NULL) {
176 printf("assert getprop(\"ro.product.device\") == \"%s\" || "
177 "getprop(\"ro.build.product\") == \"%s\"\n", device, device);
178 }
179
180 // scan android-info.txt to enforce compatibility with the target system
181 FILE *fp = fopen(argv[2], "r");
182 if (fp == NULL) {
183 perror(argv[2]);
184 return 1;
185 }
186
187 // The lines we're looking for look like:
Doug Zongkerbc012de2009-03-24 21:30:32 -0700188 // version-bootloader=x.yy.zzzz|x.yy.zzzz|...
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800189 // or:
Doug Zongkerbc012de2009-03-24 21:30:32 -0700190 // require version-bootloader=x.yy.zzzz|x.yy.zzzz|...
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800191 char line[256];
192 while (fgets(line, sizeof(line), fp)) {
Doug Zongkerbc012de2009-03-24 21:30:32 -0700193 const char *name = strtok(line, "="), *value = strtok(NULL, "|\n");
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800194 if (value != NULL &&
195 (!strcmp(name, "version-bootloader") ||
196 !strcmp(name, "require version-bootloader"))) {
Doug Zongkerbc012de2009-03-24 21:30:32 -0700197 printf("assert getprop(\"ro.bootloader\") == \"%s\"", value);
198
199 while ((value = strtok(NULL, "|\n")) != NULL) {
200 printf(" || getprop(\"ro.bootloader\") == \"%s\"", value);
201 }
202 printf("\n");
The Android Open Source Projectc24a8e62009-03-03 19:28:42 -0800203 }
204 // We also used to check version-baseband, but we update radio.img
205 // ourselves, so there's no need.
206 }
207
208 // erase the boot sector first, so if the update gets interrupted,
209 // the system will reboot into the recovery partition and start over.
210 printf("format BOOT:\n");
211
212 // write the radio image (actually just loads it into RAM for now)
213 printf("show_progress 0.1 0\n");
214 printf("write_radio_image PACKAGE:radio.img\n");
215
216 // erase and reinstall the system image
217 printf("show_progress 0.5 0\n");
218 printf("format SYSTEM:\n");
219 printf("copy_dir PACKAGE:system SYSTEM:\n");
220
221 // walk the files in the system image, set their permissions, etc.
222 // use -1 for default values to force permissions to be set explicitly.
223 walk_files(argv[1], "", -1, -1, -1, -1);
224
225 // as the last step, write the boot sector.
226 printf("show_progress 0.2 0\n");
227 printf("write_raw_image PACKAGE:boot.img BOOT:\n");
228
229 // after the end of the script, the radio will be written to cache
230 // leave some space in the progress bar for this operation
231 printf("show_progress 0.2 10\n");
232 return 0;
233}