/mnt/asec目录

Google Android手机的软件为了安全性和稳定性都是默认安装到手机内存里,但是手机内存有限,所以我们会做app2sd操作,来让我们安装的软件放到sd卡上,这个操作是需要rom的支持的。
    Android 2.2 可以将手机程序安装在外置的sd卡上,也就是我们平常所说的app2sd。但是,官方的app2sd非常鸡肋,需要软件自身支持安装在内存卡上才可以,也就是说用官方的app2sd,要把程序安装在内存卡上,并不是我们使用者说了算,而是软件开发者说了算。经测试安装60多个软件,其中仅有可怜的5个程序能使用官方的app2sd安装在内存卡上。所以,官方的这个app2sd就是忽悠人的。当然,现在很多第三方ROM都自带了第三方的app2sd,可以将任何程序都安装在sd卡上。
    在正式介绍app2sd之前,我先要介绍下android系统的几个比较重要的目录,这是理解后面内容的基础。

    /system 存放的是rom的信息;/system/app 存放rom本身附带的软件即系统软件;/system/data 存放/system/app 中核心系统软件的数据文件信息。
    /data 存放的是用户的软件信息(非自带rom安装的软件);/data/app 存放用户安装的软件;/data/data 存放所有软件(包括/system/app 和 /data/app 和 /mnt/asec中装的软件)的一些lib和xml文件等数据信息;/data/dalvik-cache 存放程序的缓存文件,这里的文件都是可以删除的。

    /mnt 目录,熟悉linux的人都清楚,linux默认挂载外部设备都会挂到这个目录下面去,如将sd卡挂载上去后,会生成一个/mnt/sdcard 目录。
    /sdcard 目录,这是一个软链接(相当于windows的文件夹的快捷方式),链接到/mnt/sdcard 目录,即这个目录的内容就是sdcard的内容。
    在Android 2.2之后的版本允许将应用程序安装于SD卡,每一个安装在SD卡的应用程序,都可以在SD卡中的/sdcard/.android_secure 目录里找到名称中有出现它的程序名,和副文件名为asec的经过特殊加密处理后的档案。当SD卡挂载于手机时,/mnt/sdcard/.android_secure 目录会被映射到/mnt/asec 目录和 /mnt/secure 目录。其中/mnt/asec 目录中主要是程序的安装目录,包括其执行文件和lib文件等;而/mnt/secure 目录中就存放程序加密后的档案。也就是说,在/mnt路径下看到的/mnt/asec目录和/mnt/secure目录并不是真正存在在手机内存或者sd卡的分区挂载目录,它们只是/mnt/sdcard/.android_secure目录的一个影像而已。
    因此,用户程序安装到到sd卡上后,其内容可能分散到:/mnt/asec , /mnt/secure , /data/data 。

    要实现app2sd,目前比较流行有两种方案,分别是app2ext 和 data2ext,下面分别介绍下这2种方案。

    在Linux文件系统中,有一种特别的文件叫“软链接”,类似于Windows下的快捷方式,软链接可以把一个文件或者文件夹映射到别的地方,一个例子如上面介绍的/sdcard 就是/mnt/sdcard 的软链接。

    app2ext的原理是,删除data区中的app文件夹,然后在sd卡的ext分区上创建一个app文件,并通过软链接映射到data区。这样系统会以为,app这个软链接是一个真实的文件夹,会把程序都安装在里面,但实际上,这些程序都安装到卡上了。但由于操作系统并不知道,所以这种情况下,我们依然看到系统显示这个程序是安装在“内置空间”的。
    data2ext则更彻底,它不是用软链接,而是直接用“挂载”功能,Linux下所有的存储设备都必须挂载成一个文件夹才能进行文件操作(如sd卡就挂载在/mnt/sdcard目录下面)。data文件夹本来是对应手机内部Flash中的一个分区(为了保持术语的准确,这里要把内部Flash和内存相区别,内部Flash是ROM,内存是RAM)。而data2ext则是修改了挂载对应关系,使data文件夹挂载的不是内置Flash,而是sd卡的整个ext分区。这样,不仅是app,连存储程序设置的data和缓存dalvik-cache都会存储到sd卡中。

    可以看到,dalvik-cache和data这两个文件夹的位置,是这两种方式的一个重大区别。其中dalvik-cache是虚拟机预编译缓存,data(不同于/data,这个是/data/data)是存储程序数据的地方,例如游戏的存档记录,软件的配置信息等。这样有什么区别,区别在于假如你重刷了ROM,app2ext的话,所有的程序都可以保留,但是这些程序的配置信息和游戏的存档都会丢失。而data2ext则可以连同配置和存档都保留,但是dalvik-cache也是一个容易积累垃圾的地方,这些垃圾也会一同保留。
    data2ext由于是把整个data分区都放在sd卡上,因此,我们刷ROM需要WIPE的时候,这个data分区的内容就可能不会被wipe,这可以保存用户的个人资料,但是也可能造成系统莫名其妙的故障。
int FirstStageMain(int argc, char** argv) { 185 if (REBOOT_BOOTLOADER_ON_PANIC) { 186 InstallRebootSignalHandlers(); 187 } 188 189 boot_clock::time_point start_time = boot_clock::now(); 190 191 std::vector<std::pair<std::string, int>> errors; 192 #define CHECKCALL(x) \ 193 if ((x) != 0) errors.emplace_back(#x " failed", errno); 194 195 // Clear the umask. 196 umask(0); 197 198 CHECKCALL(clearenv()); 199 CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1)); 200 // Get the basic filesystem setup we need put together in the initramdisk 201 // on / and then we'll let the rc file figure out the rest. 202 CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755")); 203 CHECKCALL(mkdir("/dev/pts", 0755)); 204 CHECKCALL(mkdir("/dev/socket", 0755)); 205 CHECKCALL(mkdir("/dev/dm-user", 0755)); 206 CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL)); 207 #define MAKE_STR(x) __STRING(x) 208 CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC))); 209 #undef MAKE_STR 210 // Don't expose the raw commandline to unprivileged processes. 211 CHECKCALL(chmod("/proc/cmdline", 0440)); 212 std::string cmdline; 213 android::base::ReadFileToString("/proc/cmdline", &cmdline); 214 // Don't expose the raw bootconfig to unprivileged processes. 215 chmod("/proc/bootconfig", 0440); 216 std::string bootconfig; 217 android::base::ReadFileToString("/proc/bootconfig", &bootconfig); 218 gid_t groups[] = {AID_READPROC}; 219 CHECKCALL(setgroups(arraysize(groups), groups)); 220 CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL)); 221 CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL)); 222 223 CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11))); 224 225 if constexpr (WORLD_WRITABLE_KMSG) { 226 CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11))); 227 } 228 229 CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8))); 230 CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9))); 231 232 // This is needed for log wrapper, which gets called before ueventd runs. 233 CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2))); 234 CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3))); 235 236 // These below mounts are done in first stage init so that first stage mount can mount 237 // subdirectories of /mnt/{vendor,product}/. Other mounts, not required by first stage mount, 238 // should be done in rc files. 239 // Mount staging areas for devices managed by vold 240 // See storage config details at http://source.android.com/devices/storage/ 241 CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV, 242 "mode=0755,uid=0,gid=1000")); 243 // /mnt/vendor is used to mount vendor-specific partitions that can not be 244 // part of the vendor partition, e.g. because they are mounted read-write. 245 CHECKCALL(mkdir("/mnt/vendor", 0755)); 246 // /mnt/product is used to mount product-specific partitions that can not be 247 // part of the product partition, e.g. because they are mounted read-write. 248 CHECKCALL(mkdir("/mnt/product", 0755)); 249 250 // /debug_ramdisk is used to preserve additional files from the debug ramdisk 251 CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV, 252 "mode=0755,uid=0,gid=0")); 253 254 // /second_stage_resources is used to preserve files from first to second 255 // stage init 256 CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV, 257 "mode=0755,uid=0,gid=0")) 258 //#ifdef OPLUS_FEATURE_PHOENIX 259 //martin.li@kernel.stability, 2021/07/21, add for oplusreserve* mount point 260 CHECKCALL(mkdir("/mnt/oplus", 0755)); 261 //#endif 262 #undef CHECKCALL 263 264 SetStdioToDevNull(argv); 265 // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually 266 // talk to the outside world... 267 InitKernelLogging(argv); 268 269 if (!errors.empty()) { 270 for (const auto& [error_string, error_errno] : errors) { 271 LOG(ERROR) << error_string << " " << strerror(error_errno); 272 } 273 LOG(FATAL) << "Init encountered errors starting first stage, aborting"; 274 } 275 276 LOG(INFO) << "init first stage started!"; 277 278 auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir}; 279 if (!old_root_dir) { 280 PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk"; 281 } 282 283 struct stat old_root_info; 284 if (stat("/", &old_root_info) != 0) { 285 PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk"; 286 old_root_dir.reset(); 287 } 288 289 auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0; 290 291 boot_clock::time_point module_start_time = boot_clock::now(); 292 int module_count = 0; 293 if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console, 294 module_count)) { 295 if (want_console != FirstStageConsoleParam::DISABLED) { 296 LOG(ERROR) << "Failed to load kernel modules, starting console"; 297 } else { 298 LOG(FATAL) << "Failed to load kernel modules"; 299 } 300 } 301 if (module_count > 0) { 302 auto module_elapse_time = std::chrono::duration_cast<std::chrono::milliseconds>( 303 boot_clock::now() - module_start_time); 304 setenv(kEnvInitModuleDurationMs, std::to_string(module_elapse_time.count()).c_str(), 1); 305 LOG(INFO) << "Loaded " << module_count << " kernel modules took " 306 << module_elapse_time.count() << " ms"; 307 } 308 309 310 bool created_devices = false; 311 if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) { 312 if (!IsRecoveryMode()) { 313 created_devices = DoCreateDevices(); 314 if (!created_devices){ 315 LOG(ERROR) << "Failed to create device nodes early"; 316 } 317 } 318 StartConsole(cmdline); 319 } 320 321 if (access(kBootImageRamdiskProp, F_OK) == 0) { 322 std::string dest = GetRamdiskPropForSecondStage(); 323 std::string dir = android::base::Dirname(dest); 324 std::error_code ec; 325 if (!fs::create_directories(dir, ec) && !!ec) { 326 LOG(FATAL) << "Can't mkdir " << dir << ": " << ec.message(); 327 } 328 if (!fs::copy_file(kBootImageRamdiskProp, dest, ec)) { 329 LOG(FATAL) << "Can't copy " << kBootImageRamdiskProp << " to " << dest << ": " 330 << ec.message(); 331 } 332 LOG(INFO) << "Copied ramdisk prop to " << dest; 333 } 334 335 // If "/force_debuggable" is present, the second-stage init will use a userdebug 336 // sepolicy and load adb_debug.prop to allow adb root, if the device is unlocked. 337 if (access("/force_debuggable", F_OK) == 0) { 338 std::error_code ec; // to invoke the overloaded copy_file() that won't throw. 339 if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) || 340 !fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) { 341 LOG(ERROR) << "Failed to setup debug ramdisk"; 342 } else { 343 // setenv for second-stage init to read above kDebugRamdisk* files. 344 setenv("INIT_FORCE_DEBUGGABLE", "true", 1); 345 } 346 } 347 348 if (ForceNormalBoot(cmdline, bootconfig)) { 349 mkdir("/first_stage_ramdisk", 0755); 350 // SwitchRoot() must be called with a mount point as the target, so we bind mount the 351 // target directory to itself here. 352 if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) { 353 LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself"; 354 } 355 SwitchRoot("/first_stage_ramdisk"); 356 } 357 358 if (!DoFirstStageMount(!created_devices)) { 359 LOG(FATAL) << "Failed to mount required partitions early ..."; 360 } 361 362 struct stat new_root_info; 363 if (stat("/", &new_root_info) != 0) { 364 PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk"; 365 old_root_dir.reset(); 366 } 367 368 if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) { 369 FreeRamdisk(old_root_dir.get(), old_root_info.st_dev); 370 } 371 372 SetInitAvbVersionInRecovery(); 373 374 setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 375 1); 376 LOG(INFO) << "init first stage completed!"; 377 378 const char* path = "/system/bin/init"; 379 const char* args[] = {path, "selinux_setup", nullptr}; 380 auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC); 381 dup2(fd, STDOUT_FILENO); 382 dup2(fd, STDERR_FILENO); 383 close(fd); 384 execv(path, const_cast<char**>(args)); 385 386 // execv() only returns if an error happened, in which case we 387 // panic and never fall through this conditional. 388 PLOG(FATAL) << "execv(\"" << path << "\") failed"; 389 390 return 1; 391 } 392 393 } // namespace init 394 } // namespace android 395
07-22
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值