Merge "Refactor OWNERS files for Android Core Networking"
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 4a53a33..76c5ade 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -115,19 +115,14 @@
"libbase",
"libdebuggerd",
"libunwindstack",
- "libdexfile", // libunwindstack dependency
- "libdexfile_external", // libunwindstack dependency
- "libdexfile_support", // libunwindstack dependency
+ "libdexfile_support_static", // libunwindstack dependency
"liblzma",
"libcutils",
],
target: {
recovery: {
exclude_static_libs: [
- "libartbase",
- "libdexfile",
- "libdexfile_external",
- "libdexfile_support",
+ "libdexfile_support_static",
],
},
},
@@ -174,8 +169,7 @@
include_dirs: ["bionic/libc"],
static_libs: [
- "libdexfile_external", // libunwindstack dependency
- "libdexfile_support", // libunwindstack dependency
+ "libdexfile_support_static", // libunwindstack dependency
"libunwindstack",
"liblzma",
"libbase",
@@ -185,8 +179,7 @@
target: {
recovery: {
exclude_static_libs: [
- "libdexfile_external",
- "libdexfile_support",
+ "libdexfile_support_static",
],
},
},
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 4d44fcf..4659add 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -588,18 +588,7 @@
return boot_devices;
}
-void EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
- auto iter = std::remove_if(fstab->begin(), fstab->end(),
- [&](const auto& entry) { return entry.mount_point == mount_point; });
- fstab->erase(iter, fstab->end());
-}
-
-void TransformFstabForGsi(Fstab* fstab) {
- EraseFstabEntry(fstab, "/system");
- EraseFstabEntry(fstab, "/data");
-
- fstab->emplace_back(BuildGsiSystemFstabEntry());
-
+FstabEntry BuildGsiUserdataFstabEntry() {
constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
FstabEntry userdata = {
@@ -615,6 +604,34 @@
userdata.fs_mgr_flags.quota = true;
userdata.fs_mgr_flags.late_mount = true;
userdata.fs_mgr_flags.formattable = true;
+ return userdata;
+}
+
+void EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
+ auto iter = std::remove_if(fstab->begin(), fstab->end(),
+ [&](const auto& entry) { return entry.mount_point == mount_point; });
+ fstab->erase(iter, fstab->end());
+}
+
+void TransformFstabForGsi(Fstab* fstab) {
+ // Inherit fstab properties for userdata.
+ FstabEntry userdata;
+ if (FstabEntry* entry = GetEntryForMountPoint(fstab, "/data")) {
+ userdata = *entry;
+ userdata.blk_device = "userdata_gsi";
+ userdata.fs_mgr_flags.logical = true;
+ userdata.fs_mgr_flags.formattable = true;
+ if (!userdata.key_dir.empty()) {
+ userdata.key_dir += "/gsi";
+ }
+ } else {
+ userdata = BuildGsiUserdataFstabEntry();
+ }
+
+ EraseFstabEntry(fstab, "/system");
+ EraseFstabEntry(fstab, "/data");
+
+ fstab->emplace_back(BuildGsiSystemFstabEntry());
fstab->emplace_back(userdata);
}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 87729cd..9364b2d 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -78,6 +78,10 @@
#if ALLOW_ADBD_DISABLE_VERITY == 0 // If we are a user build, provide stubs
+Fstab fs_mgr_overlayfs_candidate_list(const Fstab&) {
+ return {};
+}
+
bool fs_mgr_overlayfs_mount_all(Fstab*) {
return false;
}
@@ -238,8 +242,7 @@
return ret;
}
-const char* fs_mgr_mount_point(const char* mount_point) {
- if (!mount_point) return mount_point;
+const std::string fs_mgr_mount_point(const std::string& mount_point) {
if ("/"s != mount_point) return mount_point;
return "/system";
}
@@ -526,40 +529,6 @@
}
}
-std::vector<std::string> fs_mgr_candidate_list(Fstab* fstab, const char* mount_point = nullptr) {
- std::vector<std::string> mounts;
- for (auto& entry : *fstab) {
- if (!fs_mgr_overlayfs_already_mounted(entry.mount_point) &&
- !fs_mgr_wants_overlayfs(&entry)) {
- continue;
- }
- std::string new_mount_point(fs_mgr_mount_point(entry.mount_point.c_str()));
- if (mount_point && (new_mount_point != mount_point)) continue;
-
- auto saved_errno = errno;
- auto verity_enabled = fs_mgr_is_verity_enabled(entry);
- if (errno == ENOENT || errno == ENXIO) errno = saved_errno;
- if (verity_enabled) continue;
-
- auto duplicate_or_more_specific = false;
- for (auto it = mounts.begin(); it != mounts.end();) {
- if ((*it == new_mount_point) ||
- (android::base::StartsWith(new_mount_point, *it + "/"))) {
- duplicate_or_more_specific = true;
- break;
- }
- if (android::base::StartsWith(*it, new_mount_point + "/")) {
- it = mounts.erase(it);
- } else {
- ++it;
- }
- }
- if (!duplicate_or_more_specific) mounts.emplace_back(new_mount_point);
- }
-
- return mounts;
-}
-
// Mount kScratchMountPoint
bool fs_mgr_overlayfs_mount_scratch(const std::string& device_path, const std::string mnt_type,
bool readonly = false) {
@@ -802,12 +771,42 @@
} // namespace
+Fstab fs_mgr_overlayfs_candidate_list(const Fstab& fstab) {
+ Fstab candidates;
+ for (const auto& entry : fstab) {
+ FstabEntry new_entry = entry;
+ if (!fs_mgr_overlayfs_already_mounted(entry.mount_point) &&
+ !fs_mgr_wants_overlayfs(&new_entry)) {
+ continue;
+ }
+ auto new_mount_point = fs_mgr_mount_point(entry.mount_point);
+ auto duplicate_or_more_specific = false;
+ for (auto it = candidates.begin(); it != candidates.end();) {
+ auto it_mount_point = fs_mgr_mount_point(it->mount_point);
+ if ((it_mount_point == new_mount_point) ||
+ (android::base::StartsWith(new_mount_point, it_mount_point + "/"))) {
+ duplicate_or_more_specific = true;
+ break;
+ }
+ if (android::base::StartsWith(it_mount_point, new_mount_point + "/")) {
+ it = candidates.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ if (!duplicate_or_more_specific) candidates.emplace_back(std::move(new_entry));
+ }
+ return candidates;
+}
+
bool fs_mgr_overlayfs_mount_all(Fstab* fstab) {
auto ret = false;
if (fs_mgr_overlayfs_invalid()) return ret;
auto scratch_can_be_mounted = true;
- for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
+ for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
+ if (fs_mgr_is_verity_enabled(entry)) continue;
+ auto mount_point = fs_mgr_mount_point(entry.mount_point);
if (fs_mgr_overlayfs_already_mounted(mount_point)) {
ret = true;
continue;
@@ -840,8 +839,9 @@
return {};
}
- for (const auto& mount_point : fs_mgr_candidate_list(fstab)) {
- if (fs_mgr_overlayfs_already_mounted(mount_point)) continue;
+ for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {
+ if (fs_mgr_is_verity_enabled(entry)) continue;
+ if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) continue;
auto device = fs_mgr_overlayfs_scratch_device();
if (!fs_mgr_overlayfs_scratch_can_be_mounted(device)) break;
return {device};
@@ -867,8 +867,24 @@
return false;
}
errno = save_errno;
- auto mounts = fs_mgr_candidate_list(&fstab, fs_mgr_mount_point(mount_point));
- if (mounts.empty()) return ret;
+ auto candidates = fs_mgr_overlayfs_candidate_list(fstab);
+ for (auto it = candidates.begin(); it != candidates.end();) {
+ if (mount_point &&
+ (fs_mgr_mount_point(it->mount_point) != fs_mgr_mount_point(mount_point))) {
+ it = candidates.erase(it);
+ continue;
+ }
+ save_errno = errno;
+ auto verity_enabled = fs_mgr_is_verity_enabled(*it);
+ if (errno == ENOENT || errno == ENXIO) errno = save_errno;
+ if (verity_enabled) {
+ it = candidates.erase(it);
+ continue;
+ }
+ ++it;
+ }
+
+ if (candidates.empty()) return ret;
std::string dir;
for (const auto& overlay_mount_point : kOverlayMountPoints) {
@@ -891,8 +907,8 @@
std::string overlay;
ret |= fs_mgr_overlayfs_setup_dir(dir, &overlay, change);
- for (const auto& fsrec_mount_point : mounts) {
- ret |= fs_mgr_overlayfs_setup_one(overlay, fsrec_mount_point, change);
+ for (const auto& entry : candidates) {
+ ret |= fs_mgr_overlayfs_setup_one(overlay, fs_mgr_mount_point(entry.mount_point), change);
}
return ret;
}
@@ -901,7 +917,6 @@
// If something is altered, set *change.
bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
if (change) *change = false;
- mount_point = fs_mgr_mount_point(mount_point);
auto ret = true;
// If scratch exists, but is not mounted, lets gain access to clean
// specific override entries.
@@ -919,7 +934,8 @@
fs_mgr_overlayfs_scratch_mount_type());
}
for (const auto& overlay_mount_point : kOverlayMountPoints) {
- ret &= fs_mgr_overlayfs_teardown_one(overlay_mount_point, mount_point ?: "", change);
+ ret &= fs_mgr_overlayfs_teardown_one(
+ overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "", change);
}
if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
// After obligatory teardown to make sure everything is clean, but if
@@ -946,8 +962,9 @@
return false;
}
if (fs_mgr_overlayfs_invalid()) return false;
- for (const auto& mount_point : fs_mgr_candidate_list(&fstab)) {
- if (fs_mgr_overlayfs_already_mounted(mount_point)) return true;
+ for (const auto& entry : fs_mgr_overlayfs_candidate_list(fstab)) {
+ if (fs_mgr_is_verity_enabled(entry)) continue;
+ if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) return true;
}
return false;
}
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 64682cc..6aaf1f3 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -21,6 +21,8 @@
#include <string>
#include <vector>
+android::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);
+
bool fs_mgr_overlayfs_mount_all(android::fs_mgr::Fstab* fstab);
std::vector<std::string> fs_mgr_overlayfs_required_devices(android::fs_mgr::Fstab* fstab);
bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index 8298bf2..7fc9518 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -210,6 +210,21 @@
fi
}
+[ "USAGE: usb_status > stdout
+
+If adb_wait failed, check if device is in fastboot mode and report status
+
+Returns: \"(USB stack borken?)\", \"(In fastboot mode)\" or \"(in adb mode)\"" ]
+usb_status() {
+ if inFastboot; then
+ echo "(In fastboot mode)"
+ elif inAdb; then
+ echo "(In adb mode)"
+ else
+ echo "(USB stack borken?)"
+ fi
+}
+
[ "USAGE: fastboot_wait [timeout]
Returns: waits until the device has returned for fastboot or optional timeout" ]
@@ -383,6 +398,17 @@
-e " /\(cache\|mnt/scratch\|mnt/vendor/persist\|persist\|metadata\) "
}
+[ "USAGE: skip_unrelated_mounts < /proc/mounts
+
+or output from df
+
+Filters out all apex and vendor override administrative overlay mounts
+uninteresting to the test" ]
+skip_unrelated_mounts() {
+ grep -v "^overlay.* /\(apex\|bionic\|system\|vendor\)/[^ ]" |
+ grep -v "[%] /\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$"
+}
+
##
## MAINLINE
##
@@ -484,9 +510,9 @@
echo "${ORANGE}[ WARNING ]${NORMAL} rebooting before test" >&2
adb_reboot &&
adb_wait 2m ||
- die "lost device after reboot after wipe (USB stack broken?)"
+ die "lost device after reboot after wipe `usb_status`"
adb_root ||
- die "lost device after elevation to root after wipe (USB stack broken?)"
+ die "lost device after elevation to root after wipe `usb_status`"
fi
D=`adb_sh df -k </dev/null` &&
H=`echo "${D}" | head -1` &&
@@ -509,7 +535,8 @@
grep "Filesystem features:.*shared_blocks" >/dev/null &&
no_dedupe=false
done
-D=`adb_sh df -k ${D} </dev/null`
+D=`adb_sh df -k ${D} </dev/null |
+ sed 's@\([%] /\)\(apex\|bionic\|system\|vendor\)/[^ ][^ ]*$@\1@'`
echo "${D}"
if [ X"${D}" = X"${D##* 100[%] }" ] && ${no_dedupe} ; then
overlayfs_needed=false
@@ -548,9 +575,9 @@
L=`adb_logcat -b all -v nsec -t ${T} 2>&1`
adb_reboot &&
adb_wait 2m ||
- die "lost device after reboot requested (USB stack broken?)"
+ die "lost device after reboot requested `usb_status`"
adb_root ||
- die "lost device after elevation to root (USB stack broken?)"
+ die "lost device after elevation to root `usb_status`"
rebooted=true
# re-disable verity to see the setup remarks expected
T=`adb_date`
@@ -593,7 +620,7 @@
die -t "${T}" "adb remount failed"
D=`adb_sh df -k </dev/null` &&
H=`echo "${D}" | head -1` &&
- D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` ||
+ D=`echo "${D}" | skip_unrelated_mounts | grep "^overlay "` ||
( [ -n "${L}" ] && echo "${L}" && false )
ret=${?}
uses_dynamic_scratch=false
@@ -637,7 +664,7 @@
echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
die "overlay takeover after remount"
!(adb_sh grep "^overlay " /proc/mounts </dev/null |
- grep -v "^overlay /\(vendor\|system\|bionic\)/..* overlay ro," |
+ skip_unrelated_mounts |
grep " overlay ro,") &&
!(adb_sh grep " rw," /proc/mounts </dev/null |
skip_administrative_mounts data) ||
@@ -694,7 +721,7 @@
adb_su sed -n '1,/overlay \/system/p' /proc/mounts </dev/null |
skip_administrative_mounts |
- grep -v ' \(squashfs\|ext4\|f2fs\) ' &&
+ grep -v ' \(squashfs\|ext4\|f2fs\|vfat\) ' &&
echo "${ORANGE}[ WARNING ]${NORMAL} overlay takeover after first stage init" >&2 ||
echo "${GREEN}[ OK ]${NORMAL} overlay takeover in first stage init" >&2
fi
@@ -782,7 +809,7 @@
adb_root &&
D=`adb_sh df -k </dev/null` &&
H=`echo "${D}" | head -1` &&
- D=`echo "${D}" | grep -v " /vendor/..*$" | grep "^overlay "` &&
+ D=`echo "${D}" | skip_unrelated_mounts | grep "^overlay "` &&
echo "${H}" &&
echo "${D}" &&
echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
@@ -888,7 +915,7 @@
# Prerequisite is a prepped device from above.
adb_reboot &&
adb_wait 2m ||
- die "lost device after reboot to ro state (USB stack broken?)"
+ die "lost device after reboot to ro state `usb_status`"
adb_sh grep " /vendor .* rw," /proc/mounts >/dev/null &&
die "/vendor is not read-only"
adb_su mount -o rw,remount /vendor ||
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 1b077bc..898e28e 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -47,6 +47,7 @@
using android::base::Split;
using android::base::Timer;
using android::fs_mgr::AvbHandle;
+using android::fs_mgr::AvbHandleStatus;
using android::fs_mgr::AvbHashtreeResult;
using android::fs_mgr::AvbUniquePtr;
using android::fs_mgr::BuildGsiSystemFstabEntry;
@@ -737,8 +738,17 @@
hashtree_result =
avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
} else if (!fstab_entry->avb_key.empty()) {
- hashtree_result =
- AvbHandle::SetUpStandaloneAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);
+ if (!InitAvbHandle()) return false;
+ // Checks if hashtree should be disabled from the top-level /vbmeta.
+ if (avb_handle_->status() == AvbHandleStatus::kHashtreeDisabled ||
+ avb_handle_->status() == AvbHandleStatus::kVerificationDisabled) {
+ LOG(ERROR) << "Top-level vbmeta is disabled, skip Hashtree setup for "
+ << fstab_entry->mount_point;
+ return true; // Returns true to mount the partition directly.
+ } else {
+ hashtree_result = AvbHandle::SetUpStandaloneAvbHashtree(
+ fstab_entry, false /* wait_for_verity_dev */);
+ }
} else {
return true; // No need AVB, returns true to mount the partition directly.
}
@@ -754,8 +764,6 @@
default:
return false;
}
-
- return true; // Returns true to mount the partition.
}
bool FirstStageMountVBootV2::InitAvbHandle() {
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index cd8ef94..d094811 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -126,22 +126,26 @@
return false;
}
- Json::Value cgroups = root["Cgroups"];
- for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {
- std::string name = cgroups[i]["Controller"].asString();
- descriptors->emplace(std::make_pair(
- name,
- CgroupDescriptor(1, name, cgroups[i]["Path"].asString(),
- std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
- cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString())));
+ if (root.isMember("Cgroups")) {
+ const Json::Value& cgroups = root["Cgroups"];
+ for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {
+ std::string name = cgroups[i]["Controller"].asString();
+ descriptors->emplace(std::make_pair(
+ name,
+ CgroupDescriptor(1, name, cgroups[i]["Path"].asString(),
+ std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
+ cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString())));
+ }
}
- Json::Value cgroups2 = root["Cgroups2"];
- descriptors->emplace(std::make_pair(
- CGROUPV2_CONTROLLER_NAME,
- CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
- std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
- cgroups2["UID"].asString(), cgroups2["GID"].asString())));
+ if (root.isMember("Cgroups2")) {
+ const Json::Value& cgroups2 = root["Cgroups2"];
+ descriptors->emplace(std::make_pair(
+ CGROUPV2_CONTROLLER_NAME,
+ CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+ std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
+ cgroups2["UID"].asString(), cgroups2["GID"].asString())));
+ }
return true;
}
diff --git a/libvndksupport/include/vndksupport/linker.h b/libvndksupport/include/vndksupport/linker.h
index f509564..5f48c39 100644
--- a/libvndksupport/include/vndksupport/linker.h
+++ b/libvndksupport/include/vndksupport/linker.h
@@ -20,6 +20,16 @@
extern "C" {
#endif
+/*
+ * Returns whether the current process is a vendor process.
+ *
+ * Note that this is only checking what process is running and has nothing to
+ * do with what namespace the caller is loaded at. For example, a VNDK-SP
+ * library loaded by SP-HAL calling this function may still get a 'false',
+ * because it is running in a system process.
+ */
+int android_is_in_vendor_process();
+
void* android_load_sphal_library(const char* name, int flag);
int android_unload_sphal_library(void* handle);
diff --git a/libvndksupport/libvndksupport.map.txt b/libvndksupport/libvndksupport.map.txt
index d3db10f..ac9a99c 100644
--- a/libvndksupport/libvndksupport.map.txt
+++ b/libvndksupport/libvndksupport.map.txt
@@ -1,5 +1,6 @@
LIBVNDKSUPPORT {
global:
+ android_is_in_vendor_process; # vndk apex
android_load_sphal_library; # vndk apex
android_unload_sphal_library; # vndk apex
local:
diff --git a/libvndksupport/linker.c b/libvndksupport/linker.c
index bc5620b..821940a 100644
--- a/libvndksupport/linker.c
+++ b/libvndksupport/linker.c
@@ -45,6 +45,17 @@
return vendor_namespace;
}
+int android_is_in_vendor_process() {
+ if (android_get_exported_namespace == NULL) {
+ ALOGD("android_get_exported_namespace() not available. Assuming system process.");
+ return 0;
+ }
+
+ // In vendor process, 'vndk' namespace is not visible, whereas in system
+ // process, it is.
+ return android_get_exported_namespace("vndk") == NULL;
+}
+
void* android_load_sphal_library(const char* name, int flag) {
struct android_namespace_t* vendor_namespace = get_vendor_namespace();
if (vendor_namespace != NULL) {
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 123fdd7..b5e5a71 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -75,6 +75,16 @@
include $(BUILD_PREBUILT)
#######################################
+# cgroups.json for recovery
+include $(CLEAR_VARS)
+LOCAL_MODULE := cgroups.recovery.json
+LOCAL_SRC_FILES := $(LOCAL_MODULE)
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/etc
+LOCAL_MODULE_STEM := cgroups.json
+include $(BUILD_PREBUILT)
+
+#######################################
# task_profiles.json
include $(CLEAR_VARS)
diff --git a/rootdir/cgroups.recovery.json b/rootdir/cgroups.recovery.json
new file mode 100644
index 0000000..f0bf5fd
--- /dev/null
+++ b/rootdir/cgroups.recovery.json
@@ -0,0 +1,9 @@
+{
+ "Cgroups": [
+ {
+ "Controller": "cpuacct",
+ "Path": "/acct",
+ "Mode": "0555"
+ }
+ ]
+}