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 "android-base/stringprintf.h"
     24 
     25 #include "base/file_utils.h"
     26 #include "base/globals.h"
     27 #include "base/logging.h"  // For VLOG.
     28 #include "base/macros.h"
     29 #include "base/os.h"
     30 #include "base/unix_file/fd_file.h"
     31 #include "base/utils.h"
     32 #include "runtime.h"
     33 
     34 namespace art {
     35 namespace gc {
     36 namespace space {
     37 
     38 // This file contains helper code for ImageSpace. It has most of the file-system
     39 // related code, including handling A/B OTA.
     40 
     41 namespace impl {
     42 
     43 // Delete the directory and its (regular or link) contents. If the recurse flag is true, delete
     44 // sub-directories recursively.
     45 static void DeleteDirectoryContents(const std::string& dir, bool recurse) {
     46   if (!OS::DirectoryExists(dir.c_str())) {
     47     return;
     48   }
     49   DIR* c_dir = opendir(dir.c_str());
     50   if (c_dir == nullptr) {
     51     PLOG(WARNING) << "Unable to open " << dir << " to delete it's contents";
     52     return;
     53   }
     54 
     55   for (struct dirent* de = readdir(c_dir); de != nullptr; de = readdir(c_dir)) {
     56     const char* name = de->d_name;
     57     if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
     58       continue;
     59     }
     60     // We only want to delete regular files and symbolic links.
     61     std::string file = android::base::StringPrintf("%s/%s", dir.c_str(), name);
     62     if (de->d_type != DT_REG && de->d_type != DT_LNK) {
     63       if (de->d_type == DT_DIR) {
     64         if (recurse) {
     65           DeleteDirectoryContents(file, recurse);
     66           // Try to rmdir the directory.
     67           if (rmdir(file.c_str()) != 0) {
     68             PLOG(ERROR) << "Unable to rmdir " << file;
     69           }
     70         }
     71       } else {
     72         LOG(WARNING) << "Unexpected file type of " << std::hex << de->d_type << " encountered.";
     73       }
     74     } else {
     75       // Try to unlink the file.
     76       if (unlink(file.c_str()) != 0) {
     77         PLOG(ERROR) << "Unable to unlink " << file;
     78       }
     79     }
     80   }
     81   CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory.";
     82 }
     83 
     84 }  // namespace impl
     85 
     86 
     87 // We are relocating or generating the core image. We should get rid of everything. It is all
     88 // out-of-date. We also don't really care if this fails since it is just a convenience.
     89 // Adapted from prune_dex_cache(const char* subdir) in frameworks/native/cmds/installd/commands.c
     90 // Note this should only be used during first boot.
     91 static void PruneDalvikCache(InstructionSet isa) {
     92   CHECK_NE(isa, InstructionSet::kNone);
     93   // Prune the base /data/dalvik-cache.
     94   // Note: GetDalvikCache may return the empty string if the directory doesn't
     95   // exist. It is safe to pass "" to DeleteDirectoryContents, so this is okay.
     96   impl::DeleteDirectoryContents(GetDalvikCache("."), false);
     97   // Prune /data/dalvik-cache/<isa>.
     98   impl::DeleteDirectoryContents(GetDalvikCache(GetInstructionSetString(isa)), false);
     99 
    100   // Be defensive. There should be a runtime created here, but this may be called in a test.
    101   if (Runtime::Current() != nullptr) {
    102     Runtime::Current()->SetPrunedDalvikCache(true);
    103   }
    104 }
    105 
    106 // We write out an empty file to the zygote's ISA specific cache dir at the start of
    107 // every zygote boot and delete it when the boot completes. If we find a file already
    108 // present, it usually means the boot didn't complete. We wipe the entire dalvik
    109 // cache if that's the case.
    110 static void MarkZygoteStart(const InstructionSet isa, const uint32_t max_failed_boots) {
    111   const std::string isa_subdir = GetDalvikCache(GetInstructionSetString(isa));
    112   CHECK(!isa_subdir.empty()) << "Dalvik cache not found";
    113   const std::string boot_marker = isa_subdir + "/.booting";
    114   const char* file_name = boot_marker.c_str();
    115 
    116   uint32_t num_failed_boots = 0;
    117   std::unique_ptr<File> file(OS::OpenFileReadWrite(file_name));
    118   if (file.get() == nullptr) {
    119     file.reset(OS::CreateEmptyFile(file_name));
    120 
    121     if (file.get() == nullptr) {
    122       int saved_errno = errno;
    123       PLOG(WARNING) << "Failed to create boot marker.";
    124       if (saved_errno != ENOSPC) {
    125         return;
    126       }
    127 
    128       LOG(WARNING) << "Pruning dalvik cache because of low-memory situation.";
    129       impl::DeleteDirectoryContents(isa_subdir, false);
    130 
    131       // Try once more.
    132       file.reset(OS::OpenFileReadWrite(file_name));
    133       if (file == nullptr) {
    134         PLOG(WARNING) << "Failed to create boot marker.";
    135         return;
    136       }
    137     }
    138   } else {
    139     if (!file->ReadFully(&num_failed_boots, sizeof(num_failed_boots))) {
    140       PLOG(WARNING) << "Failed to read boot marker.";
    141       file->Erase();
    142       return;
    143     }
    144   }
    145 
    146   if (max_failed_boots != 0 && num_failed_boots > max_failed_boots) {
    147     LOG(WARNING) << "Incomplete boot detected. Pruning dalvik cache";
    148     impl::DeleteDirectoryContents(isa_subdir, false);
    149   }
    150 
    151   ++num_failed_boots;
    152   VLOG(startup) << "Number of failed boots on : " << boot_marker << " = " << num_failed_boots;
    153 
    154   if (lseek(file->Fd(), 0, SEEK_SET) == -1) {
    155     PLOG(WARNING) << "Failed to write boot marker.";
    156     file->Erase();
    157     return;
    158   }
    159 
    160   if (!file->WriteFully(&num_failed_boots, sizeof(num_failed_boots))) {
    161     PLOG(WARNING) << "Failed to write boot marker.";
    162     file->Erase();
    163     return;
    164   }
    165 
    166   if (file->FlushCloseOrErase() != 0) {
    167     PLOG(WARNING) << "Failed to flush boot marker.";
    168   }
    169 }
    170 
    171 }  // namespace space
    172 }  // namespace gc
    173 }  // namespace art
    174 
    175 #endif  // ART_RUNTIME_GC_SPACE_IMAGE_SPACE_FS_H_
    176