Home | History | Annotate | Download | only in space
      1 /*
      2  * Copyright (C) 2016 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 #ifndef ART_RUNTIME_GC_SPACE_IMAGE_SPACE_FS_H_
     18 #define ART_RUNTIME_GC_SPACE_IMAGE_SPACE_FS_H_
     19 
     20 #include <dirent.h>
     21 #include <dlfcn.h>
     22 
     23 #include "base/logging.h"
     24 #include "base/macros.h"
     25 #include "base/stringprintf.h"
     26 #include "base/unix_file/fd_file.h"
     27 #include "globals.h"
     28 #include "os.h"
     29 #include "runtime.h"
     30 #include "utils.h"
     31 
     32 namespace art {
     33 namespace gc {
     34 namespace space {
     35 
     36 // This file contains helper code for ImageSpace. It has most of the file-system
     37 // related code, including handling A/B OTA.
     38 
     39 namespace impl {
     40 
     41 // Delete the directory and its (regular or link) contents. If the recurse flag is true, delete
     42 // sub-directories recursively.
     43 static void DeleteDirectoryContents(const std::string& dir, bool recurse) {
     44   if (!OS::DirectoryExists(dir.c_str())) {
     45     return;
     46   }
     47   DIR* c_dir = opendir(dir.c_str());
     48   if (c_dir == nullptr) {
     49     PLOG(WARNING) << "Unable to open " << dir << " to delete it's contents";
     50     return;
     51   }
     52 
     53   for (struct dirent* de = readdir(c_dir); de != nullptr; de = readdir(c_dir)) {
     54     const char* name = de->d_name;
     55     if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
     56       continue;
     57     }
     58     // We only want to delete regular files and symbolic links.
     59     std::string file = StringPrintf("%s/%s", dir.c_str(), name);
     60     if (de->d_type != DT_REG && de->d_type != DT_LNK) {
     61       if (de->d_type == DT_DIR) {
     62         if (recurse) {
     63           DeleteDirectoryContents(file, recurse);
     64           // Try to rmdir the directory.
     65           if (rmdir(file.c_str()) != 0) {
     66             PLOG(ERROR) << "Unable to rmdir " << file;
     67           }
     68         }
     69       } else {
     70         LOG(WARNING) << "Unexpected file type of " << std::hex << de->d_type << " encountered.";
     71       }
     72     } else {
     73       // Try to unlink the file.
     74       if (unlink(file.c_str()) != 0) {
     75         PLOG(ERROR) << "Unable to unlink " << file;
     76       }
     77     }
     78   }
     79   CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory.";
     80 }
     81 
     82 static bool HasContent(const char* dir) {
     83   if (!OS::DirectoryExists(dir)) {
     84     return false;
     85   }
     86   DIR* c_dir = opendir(dir);
     87   if (c_dir == nullptr) {
     88     PLOG(WARNING) << "Unable to open " << dir << " to delete it if empty";
     89     return false;
     90   }
     91 
     92   for (struct dirent* de = readdir(c_dir); de != nullptr; de = readdir(c_dir)) {
     93     const char* name = de->d_name;
     94     if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
     95       continue;
     96     }
     97     // Something here.
     98     CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory.";
     99     return true;
    100   }
    101   CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory.";
    102   return false;
    103 }
    104 
    105 // Delete this directory, if empty. Then repeat with the parents. Skips non-existing directories.
    106 // If stop_at isn't null, the recursion will stop when a directory with the given name is found.
    107 static void DeleteEmptyDirectoriesUpTo(const std::string& dir, const char* stop_at) {
    108   if (HasContent(dir.c_str())) {
    109     return;
    110   }
    111   if (stop_at != nullptr) {
    112     // This check isn't precise, but good enough in practice.
    113     if (EndsWith(dir, stop_at)) {
    114       return;
    115     }
    116   }
    117   if (OS::DirectoryExists(dir.c_str())) {
    118     if (rmdir(dir.c_str()) != 0) {
    119       PLOG(ERROR) << "Unable to rmdir " << dir;
    120       return;
    121     }
    122   }
    123   size_t last_slash = dir.rfind('/');
    124   if (last_slash != std::string::npos) {
    125     DeleteEmptyDirectoriesUpTo(dir.substr(0, last_slash), stop_at);
    126   }
    127 }
    128 
    129 static void MoveOTAArtifacts(const char* src, const char* trg) {
    130   DCHECK(OS::DirectoryExists(src));
    131   DCHECK(OS::DirectoryExists(trg));
    132 
    133   if (HasContent(trg)) {
    134     LOG(WARNING) << "We do not support merging caches, but the target isn't empty: " << src
    135                  << " to " << trg;
    136     return;
    137   }
    138 
    139   if (rename(src, trg) != 0) {
    140     PLOG(ERROR) << "Could not rename OTA cache " << src << " to target " << trg;
    141   }
    142 }
    143 
    144 // This is some dlopen/dlsym and hardcoded data to avoid a dependency on libselinux. Make sure
    145 // this stays in sync!
    146 static bool RelabelOTAFiles(const std::string& dalvik_cache_dir) {
    147   // We only expect selinux on devices. Don't even attempt this on the host.
    148   if (!kIsTargetBuild) {
    149     return true;
    150   }
    151 
    152   // Custom deleter, so we can use std::unique_ptr.
    153   struct HandleDeleter {
    154     void operator()(void* in) {
    155       if (in != nullptr && dlclose(in) != 0) {
    156         PLOG(ERROR) << "Could not close selinux handle.";
    157       }
    158     }
    159   };
    160 
    161   // Look for selinux library.
    162   std::unique_ptr<void, HandleDeleter> selinux_handle(dlopen("libselinux.so", RTLD_NOW));
    163   if (selinux_handle == nullptr) {
    164     // Assume everything's OK if we can't open the library.
    165     return true;
    166   }
    167   dlerror();  // Clean dlerror string.
    168 
    169   void* restorecon_ptr = dlsym(selinux_handle.get(), "selinux_android_restorecon");
    170   if (restorecon_ptr == nullptr) {
    171     // Can't find the relabel function. That's bad. Make sure the zygote fails, as we have no
    172     // other recourse to make this error obvious.
    173     const char* error_string = dlerror();
    174     LOG(FATAL) << "Could not find selinux restorecon function: "
    175                << ((error_string != nullptr) ? error_string : "(unknown error)");
    176     UNREACHABLE();
    177   }
    178 
    179   using RestoreconFn = int (*)(const char*, unsigned int);
    180   constexpr unsigned int kRecursive = 4U;
    181 
    182   RestoreconFn restorecon_fn = reinterpret_cast<RestoreconFn>(restorecon_ptr);
    183   if (restorecon_fn(dalvik_cache_dir.c_str(), kRecursive) != 0) {
    184     LOG(ERROR) << "Failed to restorecon " << dalvik_cache_dir;
    185     return false;
    186   }
    187 
    188   return true;
    189 }
    190 
    191 }  // namespace impl
    192 
    193 
    194 // We are relocating or generating the core image. We should get rid of everything. It is all
    195 // out-of-date. We also don't really care if this fails since it is just a convenience.
    196 // Adapted from prune_dex_cache(const char* subdir) in frameworks/native/cmds/installd/commands.c
    197 // Note this should only be used during first boot.
    198 static void PruneDalvikCache(InstructionSet isa) {
    199   CHECK_NE(isa, kNone);
    200   // Prune the base /data/dalvik-cache.
    201   impl::DeleteDirectoryContents(GetDalvikCacheOrDie(".", false), false);
    202   // Prune /data/dalvik-cache/<isa>.
    203   impl::DeleteDirectoryContents(GetDalvikCacheOrDie(GetInstructionSetString(isa), false), false);
    204 
    205   // Be defensive. There should be a runtime created here, but this may be called in a test.
    206   if (Runtime::Current() != nullptr) {
    207     Runtime::Current()->SetPrunedDalvikCache(true);
    208   }
    209 }
    210 
    211 // We write out an empty file to the zygote's ISA specific cache dir at the start of
    212 // every zygote boot and delete it when the boot completes. If we find a file already
    213 // present, it usually means the boot didn't complete. We wipe the entire dalvik
    214 // cache if that's the case.
    215 static void MarkZygoteStart(const InstructionSet isa, const uint32_t max_failed_boots) {
    216   const std::string isa_subdir = GetDalvikCacheOrDie(GetInstructionSetString(isa), false);
    217   const std::string boot_marker = isa_subdir + "/.booting";
    218   const char* file_name = boot_marker.c_str();
    219 
    220   uint32_t num_failed_boots = 0;
    221   std::unique_ptr<File> file(OS::OpenFileReadWrite(file_name));
    222   if (file.get() == nullptr) {
    223     file.reset(OS::CreateEmptyFile(file_name));
    224 
    225     if (file.get() == nullptr) {
    226       PLOG(WARNING) << "Failed to create boot marker.";
    227       return;
    228     }
    229   } else {
    230     if (!file->ReadFully(&num_failed_boots, sizeof(num_failed_boots))) {
    231       PLOG(WARNING) << "Failed to read boot marker.";
    232       file->Erase();
    233       return;
    234     }
    235   }
    236 
    237   if (max_failed_boots != 0 && num_failed_boots > max_failed_boots) {
    238     LOG(WARNING) << "Incomplete boot detected. Pruning dalvik cache";
    239     impl::DeleteDirectoryContents(isa_subdir, false);
    240   }
    241 
    242   ++num_failed_boots;
    243   VLOG(startup) << "Number of failed boots on : " << boot_marker << " = " << num_failed_boots;
    244 
    245   if (lseek(file->Fd(), 0, SEEK_SET) == -1) {
    246     PLOG(WARNING) << "Failed to write boot marker.";
    247     file->Erase();
    248     return;
    249   }
    250 
    251   if (!file->WriteFully(&num_failed_boots, sizeof(num_failed_boots))) {
    252     PLOG(WARNING) << "Failed to write boot marker.";
    253     file->Erase();
    254     return;
    255   }
    256 
    257   if (file->FlushCloseOrErase() != 0) {
    258     PLOG(WARNING) << "Failed to flush boot marker.";
    259   }
    260 }
    261 
    262 static void TryMoveOTAArtifacts(const std::string& cache_filename, bool dalvik_cache_exists) {
    263   // We really assume here global means /data/dalvik-cache, and we'll inject 'ota.' Make sure
    264   // that's true.
    265   CHECK(StartsWith(cache_filename, "/data/dalvik-cache")) << cache_filename;
    266 
    267   // Inject ota subdirectory.
    268   std::string ota_filename(cache_filename);
    269   ota_filename = ota_filename.insert(strlen("/data/"), "ota/");
    270   CHECK(StartsWith(ota_filename, "/data/ota/dalvik-cache")) << ota_filename;
    271 
    272   // See if the file exists.
    273   if (OS::FileExists(ota_filename.c_str())) {
    274     VLOG(startup) << "OTA directory does exist, checking for artifacts";
    275 
    276     size_t last_slash = ota_filename.rfind('/');
    277     CHECK_NE(last_slash, std::string::npos);
    278     std::string ota_source_dir = ota_filename.substr(0, last_slash);
    279 
    280     // We need the dalvik cache now, really.
    281     if (dalvik_cache_exists) {
    282       size_t last_cache_slash = cache_filename.rfind('/');
    283       DCHECK_NE(last_cache_slash, std::string::npos);
    284       std::string dalvik_cache_target_dir = cache_filename.substr(0, last_cache_slash);
    285 
    286       // First clean the target cache.
    287       impl::DeleteDirectoryContents(dalvik_cache_target_dir.c_str(), false);
    288 
    289       // Now move things over.
    290       impl::MoveOTAArtifacts(ota_source_dir.c_str(), dalvik_cache_target_dir.c_str());
    291 
    292       // Last step: ensure the files have the right selinux label.
    293       if (!impl::RelabelOTAFiles(dalvik_cache_target_dir)) {
    294         // This isn't good. We potentially moved files, but they have the wrong label. Delete the
    295         // files.
    296         LOG(WARNING) << "Could not relabel files, must delete dalvik-cache.";
    297         impl::DeleteDirectoryContents(dalvik_cache_target_dir.c_str(), false);
    298       }
    299     }
    300 
    301     // Cleanup.
    302     impl::DeleteDirectoryContents(ota_source_dir.c_str(), true);
    303     impl::DeleteEmptyDirectoriesUpTo(ota_source_dir, "ota");
    304   } else {
    305     VLOG(startup) << "No OTA directory.";
    306   }
    307 }
    308 
    309 }  // namespace space
    310 }  // namespace gc
    311 }  // namespace art
    312 
    313 #endif  // ART_RUNTIME_GC_SPACE_IMAGE_SPACE_FS_H_
    314