• 作者:老汪软件技巧
  • 发表时间:2024-10-10 07:01
  • 浏览量:

本章主要讲“属性文件创建和mmap映射”,现给出完整数据流程图

上一章中讲解了上图左侧"属性安全上下文序列化",右侧部分就是“属性文件创建和mmap映射“做的工作,入口代码为__system_property_area_init

void property_init() {
    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
    CreateSerializedPropertyInfo();
    if (__system_property_area_init()) {
        LOG(FATAL) << "Failed to initialize property area";
    }
    if (!property_info_area.LoadDefaultPath()) {
        LOG(FATAL) << "Failed to load serialized property info file";
    }
}
bionic/libc/include/sys/_system_properties.h
#define PROP_FILENAME "/dev/__properties__"
// bionic/libc/include/sys/_system_properties.h 
// bionic/libc/bionic/system_property_api.cpp
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_property_area_init() {
  bool fsetxattr_failed = false;
  return system_properties.AreaInit(PROP_FILENAME, &fsetxattr_failed) && !fsetxattr_failed ? 0 : -1;
}

可以看到__system_property_area_init是调用到system_properties.AreaInit,system_properties实际是图中SystemProperties对象,它是一个大管家对象,它又如下作用:

class SystemProperties {
 public:
  friend struct LocalPropertyTestState;
  friend class SystemPropertiesTest;
  // Note that system properties are initialized before libc calls static initializers, so
  // doing any initialization in this constructor is an error.  Even a Constructor that zero
  // initializes this class will clobber the previous property initialization.
  // We rely on the static SystemProperties in libc to be placed in .bss and zero initialized.
  SystemProperties() = default;
  // Special constructor for testing that also zero initializes the important members.
  explicit SystemProperties(bool initialized) : initialized_(initialized) {
  }
}

可见system_properties实例是由libc调用初始化的。

bool SystemProperties::AreaInit(const char* filename, bool* fsetxattr_failed) {
  if (strlen(filename) >= PROP_FILENAME_MAX) {
    return false;
  }
  strcpy(property_filename_, filename);
  contexts_ = new (contexts_data_) ContextsSerialized();
  if (!contexts_->Initialize(true, property_filename_, fsetxattr_failed)) {
    return false;
  }
  initialized_ = true;
  return true;
}

在AreaInit方法中又调用了ContextsSerialized->Initialize

注意:第一个参数为writable为true表示可写的,这是一个比较重要的参数

bool ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) {
  filename_ = filename;
  if (!InitializeProperties()) {
    return false;
  }
  if (writable) {
    mkdir(filename_, S_IRWXU | S_IXGRP | S_IXOTH);
    bool open_failed = false;
    if (fsetxattr_failed) {
      *fsetxattr_failed = false;
    }
    for (size_t i = 0; i < num_context_nodes_; ++i) {
      if (!context_nodes_[i].Open(true, fsetxattr_failed)) {
        open_failed = true;
      }
    }
    if (open_failed || !MapSerialPropertyArea(true, fsetxattr_failed)) {
      FreeAndUnmap();
      return false;
    }
  } else {
    if (!MapSerialPropertyArea(false, nullptr)) {
      FreeAndUnmap();
      return false;
    }
  }

Initialize完成了橙色框中上面部分的工作:

映射程序_映射文件怎么创建_

加载property_info

bool ContextsSerialized::InitializeProperties() {
  //加载/dev/__properties__/property_info
  if (!property_info_area_file_.LoadDefaultPath()) {
    return false;
  }
  
  .....省略代码
  return true;
}
//system/core/property_service/libpropertyinfoparser/property_info_parser.cpp
bool PropertyInfoAreaFile::LoadDefaultPath() {
  return LoadPath("/dev/__properties__/property_info");
}
bool PropertyInfoAreaFile::LoadPath(const char* filename) {
  int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
  auto mmap_size = fd_stat.st_size;
  void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0);
  if (map_result == MAP_FAILED) {
    close(fd);
    return false;
  }
  auto property_info_area = reinterpret_cast(map_result);
  if (property_info_area->minimum_supported_version() > 1 ||
      property_info_area->size() != mmap_size) {
    munmap(map_result, mmap_size);
    close(fd);
    return false;
  }
  close(fd);
  mmap_base_ = map_result;
  mmap_size_ = mmap_size;
  return true;
}

创建ContextNode数组

bool ContextsSerialized::InitializeProperties() {
  .....省略代码
  
  //创建ContextNode数组
  if (!InitializeContextNodes()) {
    FreeAndUnmap();
    return false;
  }
  return true;
}
bool ContextsSerialized::InitializeContextNodes() {
  auto num_context_nodes = property_info_area_file_->num_contexts();
  auto context_nodes_mmap_size = sizeof(ContextNode) * num_context_nodes;
  // We want to avoid malloc in system properties, so we take an anonymous map instead (b/31659220).
  void* const map_result = mmap(nullptr, context_nodes_mmap_size, PROT_READ | PROT_WRITE,
                                MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (map_result == MAP_FAILED) {
    return false;
  }
  prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_result, context_nodes_mmap_size,
        "System property context nodes");
  context_nodes_ = reinterpret_cast(map_result);
  num_context_nodes_ = num_context_nodes;
  context_nodes_mmap_size_ = context_nodes_mmap_size;
  for (size_t i = 0; i < num_context_nodes; ++i) {
    new (&context_nodes_[i]) ContextNode(property_info_area_file_->context(i), filename_);
  }
  return true;
}

创建属性文件

bool ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) {
  .....省略代码
  if (writable) {
  .....省略代码
    for (size_t i = 0; i < num_context_nodes_; ++i) {
      if (!context_nodes_[i].Open(true, fsetxattr_failed)) {
        open_failed = true;
      }
    }
    
  } else {
    if (!MapSerialPropertyArea(false, nullptr)) {
      FreeAndUnmap();
      return false;
    }
  }
  return true;
}

循环遍历context_nodes_数组调用open方法创建属性文件

//调用
context_nodes_[i].Open(true, fsetxattr_failed)
//实现
bool ContextNode::Open(bool access_rw, bool* fsetxattr_failed) {
  lock_.lock();
  if (pa_) {
    lock_.unlock();
    return true;
  }
  char filename[PROP_FILENAME_MAX];
  int len = async_safe_format_buffer(filename, sizeof(filename), "%s/%s", filename_, context_);
  if (len < 0 || len >= PROP_FILENAME_MAX) {
    lock_.unlock();
    return false;
  }
  if (access_rw) {
    pa_ = prop_area::map_prop_area_rw(filename, context_, fsetxattr_failed);
  } else {
    pa_ = prop_area::map_prop_area(filename);
  }
  lock_.unlock();
  return pa_;
}

constexpr size_t PA_SIZE = 128 * 1024;
prop_area* prop_area::map_prop_area_rw(const char* filename, const char* context,
                                       bool* fsetxattr_failed) {
  /* dev is a tmpfs that we can use to carve a shared workspace
   * out of, so let's do that...
   */
  const int fd = open(filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);
  if (fd < 0) {
    if (errno == EACCES) {
      /* for consistency with the case where the process has already
       * mapped the page in and segfaults when trying to write to it
       */
      abort();
    }
    return nullptr;
  }
  if (context) {
    if (fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0) != 0) {
      async_safe_format_log(ANDROID_LOG_ERROR, "libc",
                            "fsetxattr failed to set context (%s) for \"%s\"", context, filename);
      /*
       * fsetxattr() will fail during system properties tests due to selinux policy.
       * We do not want to create a custom policy for the tester, so we will continue in
       * this function but set a flag that an error has occurred.
       * Init, which is the only daemon that should ever call this function will abort
       * when this error occurs.
       * Otherwise, the tester will ignore it and continue, albeit without any selinux
       * property separation.
       */
      if (fsetxattr_failed) {
        *fsetxattr_failed = true;
      }
    }
  }
  if (ftruncate(fd, PA_SIZE) < 0) {
    close(fd);
    return nullptr;
  }
  pa_size_ = PA_SIZE;
  pa_data_size_ = pa_size_ - sizeof(prop_area);
  void* const memory_area = mmap(nullptr, pa_size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  if (memory_area == MAP_FAILED) {
    close(fd);
    return nullptr;
  }
  prop_area* pa = new (memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION);
  close(fd);
  return pa;
}

prop_area->pa_size_:整个prop_area数据结构的大小(包含data[0])
prop_area->pa_data_size_:prop_area->data[0]可用最大空间

在/dev/__properties__下创建的属性文件如下

到此为止完整数据流程图中右侧的主要数据结构都构建完成,只剩下属性文件的数据了。