Port the current code to new IncFS
Bug: 146080380
Test: manual, "cmd incremental install-start"
Change-Id: I6761c3f0e58b6d4de1ae3c4b31c23204fba9f740
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index afce260..414c66c 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -30,6 +30,8 @@
#include <binder/BinderService.h>
#include <binder/ParcelFileDescriptor.h>
#include <binder/Status.h>
+
+#include <openssl/sha.h>
#include <sys/stat.h>
#include <uuid/uuid.h>
#include <zlib.h>
@@ -55,7 +57,6 @@
struct Constants {
static constexpr auto backing = "backing_store"sv;
static constexpr auto mount = "mount"sv;
- static constexpr auto image = "incfs.img"sv;
static constexpr auto storagePrefix = "st"sv;
static constexpr auto mountpointMdPrefix = ".mountpoint."sv;
static constexpr auto infoMdName = ".info"sv;
@@ -70,7 +71,7 @@
bool mkdirOrLog(std::string_view name, int mode = 0770, bool allowExisting = true) {
auto cstr = path::c_str(name);
if (::mkdir(cstr, mode)) {
- if (errno != EEXIST) {
+ if (!allowExisting || errno != EEXIST) {
PLOG(level) << "Can't create directory '" << name << '\'';
return false;
}
@@ -80,6 +81,11 @@
return false;
}
}
+ if (::chmod(cstr, mode)) {
+ PLOG(level) << "Changing permission failed for '" << name << '\'';
+ return false;
+ }
+
return true;
}
@@ -106,7 +112,7 @@
for (int counter = 0; counter < 1000;
mountKey.resize(prefixSize), base::StringAppendF(&mountKey, "%d", counter++)) {
auto mountRoot = path::join(incrementalDir, mountKey);
- if (mkdirOrLog(mountRoot, 0770, false)) {
+ if (mkdirOrLog(mountRoot, 0777, false)) {
return {mountKey, mountRoot};
}
}
@@ -116,11 +122,7 @@
template <class ProtoMessage, class Control>
static ProtoMessage parseFromIncfs(const IncFsWrapper* incfs, Control&& control,
std::string_view path) {
- struct stat st;
- if (::stat(path::c_str(path), &st)) {
- return {};
- }
- auto md = incfs->getMetadata(control, st.st_ino);
+ auto md = incfs->getMetadata(control, path);
ProtoMessage message;
return message.ParseFromArray(md.data(), md.size()) ? message : ProtoMessage{};
}
@@ -161,35 +163,66 @@
}
auto IncrementalService::IncFsMount::makeStorage(StorageId id) -> StorageMap::iterator {
- metadata::Storage st;
- st.set_id(id);
- auto metadata = st.SerializeAsString();
-
std::string name;
for (int no = nextStorageDirNo.fetch_add(1, std::memory_order_relaxed), i = 0;
i < 1024 && no >= 0; no = nextStorageDirNo.fetch_add(1, std::memory_order_relaxed), ++i) {
name.clear();
- base::StringAppendF(&name, "%.*s%d", int(constants().storagePrefix.size()),
- constants().storagePrefix.data(), no);
- if (auto node =
- incrementalService.mIncFs->makeDir(control, name, INCFS_ROOT_INODE, metadata);
- node >= 0) {
+ base::StringAppendF(&name, "%.*s_%d_%d", int(constants().storagePrefix.size()),
+ constants().storagePrefix.data(), id, no);
+ auto fullName = path::join(root, constants().mount, name);
+ if (auto err = incrementalService.mIncFs->makeDir(control, fullName); !err) {
std::lock_guard l(lock);
- return storages.insert_or_assign(id, Storage{std::move(name), node}).first;
+ return storages.insert_or_assign(id, Storage{std::move(fullName)}).first;
+ } else if (err != EEXIST) {
+ LOG(ERROR) << __func__ << "(): failed to create dir |" << fullName << "| " << err;
+ break;
}
}
nextStorageDirNo = 0;
return storages.end();
}
+static std::unique_ptr<DIR, decltype(&::closedir)> openDir(const char* path) {
+ return {::opendir(path), ::closedir};
+}
+
+static int rmDirContent(const char* path) {
+ auto dir = openDir(path);
+ if (!dir) {
+ return -EINVAL;
+ }
+ while (auto entry = ::readdir(dir.get())) {
+ if (entry->d_name == "."sv || entry->d_name == ".."sv) {
+ continue;
+ }
+ auto fullPath = android::base::StringPrintf("%s/%s", path, entry->d_name);
+ if (entry->d_type == DT_DIR) {
+ if (const auto err = rmDirContent(fullPath.c_str()); err != 0) {
+ PLOG(WARNING) << "Failed to delete " << fullPath << " content";
+ return err;
+ }
+ if (const auto err = ::rmdir(fullPath.c_str()); err != 0) {
+ PLOG(WARNING) << "Failed to rmdir " << fullPath;
+ return err;
+ }
+ } else {
+ if (const auto err = ::unlink(fullPath.c_str()); err != 0) {
+ PLOG(WARNING) << "Failed to delete " << fullPath;
+ return err;
+ }
+ }
+ }
+ return 0;
+}
+
void IncrementalService::IncFsMount::cleanupFilesystem(std::string_view root) {
- ::unlink(path::join(root, constants().backing, constants().image).c_str());
+ rmDirContent(path::join(root, constants().backing).c_str());
::rmdir(path::join(root, constants().backing).c_str());
::rmdir(path::join(root, constants().mount).c_str());
::rmdir(path::c_str(root));
}
-IncrementalService::IncrementalService(const ServiceManagerWrapper& sm, std::string_view rootDir)
+IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir)
: mVold(sm.getVoldService()),
mIncrementalManager(sm.getIncrementalManager()),
mIncFs(sm.getIncFs()),
@@ -205,6 +238,23 @@
// mountExistingImages();
}
+FileId IncrementalService::idFromMetadata(std::span<const uint8_t> metadata) {
+ incfs::FileId id = {};
+ if (size_t(metadata.size()) <= sizeof(id)) {
+ memcpy(&id, metadata.data(), metadata.size());
+ } else {
+ uint8_t buffer[SHA_DIGEST_LENGTH];
+ static_assert(sizeof(buffer) >= sizeof(id));
+
+ SHA_CTX ctx;
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, metadata.data(), metadata.size());
+ SHA1_Final(buffer, &ctx);
+ memcpy(&id, buffer, sizeof(id));
+ }
+ return id;
+}
+
IncrementalService::~IncrementalService() = default;
std::optional<std::future<void>> IncrementalService::onSystemReady() {
@@ -300,26 +350,36 @@
std::unique_ptr<std::string, decltype(firstCleanup)>(&mountRoot, firstCleanup);
auto mountTarget = path::join(mountRoot, constants().mount);
- if (!mkdirOrLog(path::join(mountRoot, constants().backing)) || !mkdirOrLog(mountTarget)) {
+ const auto backing = path::join(mountRoot, constants().backing);
+ if (!mkdirOrLog(backing, 0777) || !mkdirOrLog(mountTarget)) {
return kInvalidStorageId;
}
- const auto image = path::join(mountRoot, constants().backing, constants().image);
IncFsMount::Control control;
{
std::lock_guard l(mMountOperationLock);
IncrementalFileSystemControlParcel controlParcel;
- auto status = mVold->mountIncFs(image, mountTarget, incfs::truncate, &controlParcel);
+
+ if (auto err = rmDirContent(backing.c_str())) {
+ LOG(ERROR) << "Coudn't clean the backing directory " << backing << ": " << err;
+ return kInvalidStorageId;
+ }
+ if (!mkdirOrLog(path::join(backing, ".index"), 0777)) {
+ return kInvalidStorageId;
+ }
+ auto status = mVold->mountIncFs(backing, mountTarget, 0, &controlParcel);
if (!status.isOk()) {
LOG(ERROR) << "Vold::mountIncFs() failed: " << status.toString8();
return kInvalidStorageId;
}
- if (!controlParcel.cmd || !controlParcel.log) {
+ if (controlParcel.cmd.get() < 0 || controlParcel.pendingReads.get() < 0 ||
+ controlParcel.log.get() < 0) {
LOG(ERROR) << "Vold::mountIncFs() returned invalid control parcel.";
return kInvalidStorageId;
}
- control.cmdFd = controlParcel.cmd->release();
- control.logFd = controlParcel.log->release();
+ control.cmd = controlParcel.cmd.release().release();
+ control.pendingReads = controlParcel.pendingReads.release().release();
+ control.logs = controlParcel.log.release().release();
}
std::unique_lock l(mLock);
@@ -344,7 +404,7 @@
const auto storageIt = ifs->makeStorage(ifs->mountId);
if (storageIt == ifs->storages.end()) {
- LOG(ERROR) << "Can't create default storage directory";
+ LOG(ERROR) << "Can't create a default storage directory";
return kInvalidStorageId;
}
@@ -359,9 +419,12 @@
m.mutable_loader()->release_arguments();
m.mutable_loader()->release_class_name();
m.mutable_loader()->release_package_name();
- if (auto err = mIncFs->makeFile(ifs->control, constants().infoMdName, INCFS_ROOT_INODE, 0,
- metadata);
- err < 0) {
+ if (auto err =
+ mIncFs->makeFile(ifs->control,
+ path::join(ifs->root, constants().mount,
+ constants().infoMdName),
+ 0777, idFromMetadata(metadata),
+ {.metadata = {metadata.data(), (IncFsSize)metadata.size()}})) {
LOG(ERROR) << "Saving mount metadata failed: " << -err;
return kInvalidStorageId;
}
@@ -369,8 +432,8 @@
const auto bk =
(options & CreateOptions::PermanentBind) ? BindKind::Permanent : BindKind::Temporary;
- if (auto err = addBindMount(*ifs, storageIt->first, std::string(storageIt->second.name),
- std::move(mountNorm), bk, l);
+ if (auto err = addBindMount(*ifs, storageIt->first, storageIt->second.name,
+ std::string(storageIt->second.name), std::move(mountNorm), bk, l);
err < 0) {
LOG(ERROR) << "adding bind mount failed: " << -err;
return kInvalidStorageId;
@@ -419,8 +482,9 @@
const auto bk =
(options & CreateOptions::PermanentBind) ? BindKind::Permanent : BindKind::Temporary;
- if (auto err = addBindMount(*ifs, storageIt->first, std::string(storageIt->second.name),
- path::normalize(mountPoint), bk, l);
+ if (auto err = addBindMount(*ifs, storageIt->first, storageIt->second.name,
+ std::string(storageIt->second.name), path::normalize(mountPoint),
+ bk, l);
err < 0) {
LOG(ERROR) << "bindMount failed with error: " << err;
return kInvalidStorageId;
@@ -492,40 +556,36 @@
return findStorageId(path::normalize(pathInMount));
}
-Inode IncrementalService::nodeFor(StorageId storage, std::string_view subpath) const {
+FileId IncrementalService::nodeFor(StorageId storage, std::string_view subpath) const {
const auto ifs = getIfs(storage);
if (!ifs) {
- return -1;
+ return kIncFsInvalidFileId;
}
std::unique_lock l(ifs->lock);
auto storageIt = ifs->storages.find(storage);
if (storageIt == ifs->storages.end()) {
- return -1;
+ return kIncFsInvalidFileId;
}
if (subpath.empty() || subpath == "."sv) {
- return storageIt->second.node;
+ return kIncFsInvalidFileId;
}
auto path = path::join(ifs->root, constants().mount, storageIt->second.name, subpath);
l.unlock();
- struct stat st;
- if (::stat(path.c_str(), &st)) {
- return -1;
- }
- return st.st_ino;
+ return mIncFs->getFileId(ifs->control, path);
}
-std::pair<Inode, std::string_view> IncrementalService::parentAndNameFor(
+std::pair<FileId, std::string_view> IncrementalService::parentAndNameFor(
StorageId storage, std::string_view subpath) const {
auto name = path::basename(subpath);
if (name.empty()) {
- return {-1, {}};
+ return {kIncFsInvalidFileId, {}};
}
auto dir = path::dirname(subpath);
if (dir.empty() || dir == "/"sv) {
- return {-1, {}};
+ return {kIncFsInvalidFileId, {}};
}
- auto inode = nodeFor(storage, dir);
- return {inode, name};
+ auto id = nodeFor(storage, dir);
+ return {id, name};
}
IncrementalService::IfsMountPtr IncrementalService::getIfs(StorageId storage) const {
@@ -542,8 +602,8 @@
return it->second;
}
-int IncrementalService::bind(StorageId storage, std::string_view sourceSubdir,
- std::string_view target, BindKind kind) {
+int IncrementalService::bind(StorageId storage, std::string_view source, std::string_view target,
+ BindKind kind) {
if (!isValidMountTarget(target)) {
return -EINVAL;
}
@@ -552,15 +612,20 @@
if (!ifs) {
return -EINVAL;
}
+ auto normSource = path::normalize(source);
+
std::unique_lock l(ifs->lock);
const auto storageInfo = ifs->storages.find(storage);
if (storageInfo == ifs->storages.end()) {
return -EINVAL;
}
- auto source = path::join(storageInfo->second.name, sourceSubdir);
+ if (!path::startsWith(normSource, storageInfo->second.name)) {
+ return -EINVAL;
+ }
l.unlock();
std::unique_lock l2(mLock, std::defer_lock);
- return addBindMount(*ifs, storage, std::move(source), path::normalize(target), kind, l2);
+ return addBindMount(*ifs, storage, storageInfo->second.name, std::move(normSource),
+ path::normalize(target), kind, l2);
}
int IncrementalService::unbind(StorageId storage, std::string_view target) {
@@ -599,90 +664,72 @@
ifs->bindPoints.erase(bindIt);
l2.unlock();
if (!savedFile.empty()) {
- mIncFs->unlink(ifs->control, INCFS_ROOT_INODE, savedFile);
+ mIncFs->unlink(ifs->control, path::join(ifs->root, constants().mount, savedFile));
}
}
return 0;
}
-Inode IncrementalService::makeFile(StorageId storageId, std::string_view pathUnderStorage,
- long size, std::string_view metadata,
- std::string_view signature) {
- (void)signature;
- auto [parentInode, name] = parentAndNameFor(storageId, pathUnderStorage);
- if (parentInode < 0) {
- return -EINVAL;
- }
- if (auto ifs = getIfs(storageId)) {
- auto inode = mIncFs->makeFile(ifs->control, name, parentInode, size, metadata);
- if (inode < 0) {
- return inode;
+int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id,
+ incfs::NewFileParams params) {
+ if (auto ifs = getIfs(storage)) {
+ auto err = mIncFs->makeFile(ifs->control, path, mode, id, params);
+ if (err) {
+ return err;
}
- auto metadataBytes = std::vector<uint8_t>();
- if (metadata.data() != nullptr && metadata.size() > 0) {
- metadataBytes.insert(metadataBytes.end(), &metadata.data()[0],
- &metadata.data()[metadata.size()]);
+ std::vector<uint8_t> metadataBytes;
+ if (params.metadata.data && params.metadata.size > 0) {
+ metadataBytes.assign(params.metadata.data, params.metadata.data + params.metadata.size);
}
- mIncrementalManager->newFileForDataLoader(ifs->mountId, inode, metadataBytes);
- return inode;
+ mIncrementalManager->newFileForDataLoader(ifs->mountId, id, metadataBytes);
+ return 0;
}
return -EINVAL;
}
-Inode IncrementalService::makeDir(StorageId storageId, std::string_view pathUnderStorage,
- std::string_view metadata) {
- auto [parentInode, name] = parentAndNameFor(storageId, pathUnderStorage);
- if (parentInode < 0) {
- return -EINVAL;
- }
+int IncrementalService::makeDir(StorageId storageId, std::string_view path, int mode) {
if (auto ifs = getIfs(storageId)) {
- return mIncFs->makeDir(ifs->control, name, parentInode, metadata);
+ return mIncFs->makeDir(ifs->control, path, mode);
}
return -EINVAL;
}
-Inode IncrementalService::makeDirs(StorageId storageId, std::string_view pathUnderStorage,
- std::string_view metadata) {
+int IncrementalService::makeDirs(StorageId storageId, std::string_view path, int mode) {
const auto ifs = getIfs(storageId);
if (!ifs) {
return -EINVAL;
}
- std::string_view parentDir(pathUnderStorage);
- auto p = parentAndNameFor(storageId, pathUnderStorage);
- std::stack<std::string> pathsToCreate;
- while (p.first < 0) {
- parentDir = path::dirname(parentDir);
- pathsToCreate.emplace(parentDir);
- p = parentAndNameFor(storageId, parentDir);
+
+ auto err = mIncFs->makeDir(ifs->control, path, mode);
+ if (err == -EEXIST) {
+ return 0;
+ } else if (err != -ENOENT) {
+ return err;
}
- Inode inode;
- while (!pathsToCreate.empty()) {
- p = parentAndNameFor(storageId, pathsToCreate.top());
- pathsToCreate.pop();
- inode = mIncFs->makeDir(ifs->control, p.second, p.first, metadata);
- if (inode < 0) {
- return inode;
- }
+ if (auto err = makeDirs(storageId, path::dirname(path), mode)) {
+ return err;
}
- return mIncFs->makeDir(ifs->control, path::basename(pathUnderStorage), inode, metadata);
+ return mIncFs->makeDir(ifs->control, path, mode);
}
-int IncrementalService::link(StorageId storage, Inode item, Inode newParent,
- std::string_view newName) {
- if (auto ifs = getIfs(storage)) {
- return mIncFs->link(ifs->control, item, newParent, newName);
+int IncrementalService::link(StorageId sourceStorageId, std::string_view oldPath,
+ StorageId destStorageId, std::string_view newPath) {
+ if (auto ifsSrc = getIfs(sourceStorageId), ifsDest = getIfs(destStorageId);
+ ifsSrc && ifsSrc == ifsDest) {
+ return mIncFs->link(ifsSrc->control, oldPath, newPath);
}
return -EINVAL;
}
-int IncrementalService::unlink(StorageId storage, Inode parent, std::string_view name) {
+int IncrementalService::unlink(StorageId storage, std::string_view path) {
if (auto ifs = getIfs(storage)) {
- return mIncFs->unlink(ifs->control, parent, name);
+ return mIncFs->unlink(ifs->control, path);
}
return -EINVAL;
}
-int IncrementalService::addBindMount(IncFsMount& ifs, StorageId storage, std::string&& sourceSubdir,
+int IncrementalService::addBindMount(IncFsMount& ifs, StorageId storage,
+ std::string_view storageRoot, std::string&& source,
std::string&& target, BindKind kind,
std::unique_lock<std::mutex>& mainLock) {
if (!isValidMountTarget(target)) {
@@ -694,30 +741,30 @@
metadata::BindPoint bp;
bp.set_storage_id(storage);
bp.set_allocated_dest_path(&target);
- bp.set_allocated_source_subdir(&sourceSubdir);
+ bp.set_source_subdir(std::string(path::relativize(storageRoot, source)));
const auto metadata = bp.SerializeAsString();
- bp.release_source_subdir();
bp.release_dest_path();
mdFileName = makeBindMdName();
- auto node = mIncFs->makeFile(ifs.control, mdFileName, INCFS_ROOT_INODE, 0, metadata);
- if (node < 0) {
+ auto node =
+ mIncFs->makeFile(ifs.control, path::join(ifs.root, constants().mount, mdFileName),
+ 0444, idFromMetadata(metadata),
+ {.metadata = {metadata.data(), (IncFsSize)metadata.size()}});
+ if (node) {
return int(node);
}
}
- return addBindMountWithMd(ifs, storage, std::move(mdFileName), std::move(sourceSubdir),
+ return addBindMountWithMd(ifs, storage, std::move(mdFileName), std::move(source),
std::move(target), kind, mainLock);
}
int IncrementalService::addBindMountWithMd(IncrementalService::IncFsMount& ifs, StorageId storage,
- std::string&& metadataName, std::string&& sourceSubdir,
+ std::string&& metadataName, std::string&& source,
std::string&& target, BindKind kind,
std::unique_lock<std::mutex>& mainLock) {
- LOG(INFO) << "Adding bind mount: " << sourceSubdir << " -> " << target;
{
- auto path = path::join(ifs.root, constants().mount, sourceSubdir);
std::lock_guard l(mMountOperationLock);
- const auto status = mVold->bindMount(path, target);
+ const auto status = mVold->bindMount(source, target);
if (!status.isOk()) {
LOG(ERROR) << "Calling Vold::bindMount() failed: " << status.toString8();
return status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC
@@ -736,12 +783,12 @@
const auto [it, _] =
ifs.bindPoints.insert_or_assign(target,
IncFsMount::Bind{storage, std::move(metadataName),
- std::move(sourceSubdir), kind});
+ std::move(source), kind});
mBindsByPath[std::move(target)] = it;
return 0;
}
-RawMetadata IncrementalService::getMetadata(StorageId storage, Inode node) const {
+RawMetadata IncrementalService::getMetadata(StorageId storage, FileId node) const {
const auto ifs = getIfs(storage);
if (!ifs) {
return {};
@@ -831,21 +878,18 @@
LOG(INFO) << "Trying to mount: " << key;
auto mountTarget = path::join(root, constants().mount);
- const auto image = path::join(root, constants().backing, constants().image);
+ const auto backing = path::join(root, constants().backing);
IncFsMount::Control control;
IncrementalFileSystemControlParcel controlParcel;
- auto status = mVold->mountIncFs(image, mountTarget, 0, &controlParcel);
+ auto status = mVold->mountIncFs(backing, mountTarget, 0, &controlParcel);
if (!status.isOk()) {
LOG(ERROR) << "Vold::mountIncFs() failed: " << status.toString8();
return false;
}
- if (controlParcel.cmd) {
- control.cmdFd = controlParcel.cmd->release();
- }
- if (controlParcel.log) {
- control.logFd = controlParcel.log->release();
- }
+ control.cmd = controlParcel.cmd.release().release();
+ control.pendingReads = controlParcel.pendingReads.release().release();
+ control.logs = controlParcel.log.release().release();
auto ifs = std::make_shared<IncFsMount>(std::string(root), -1, std::move(control), *this);
@@ -860,8 +904,7 @@
mNextId = std::max(mNextId, ifs->mountId + 1);
std::vector<std::pair<std::string, metadata::BindPoint>> bindPoints;
- auto d = std::unique_ptr<DIR, decltype(&::closedir)>(::opendir(path::c_str(mountTarget)),
- ::closedir);
+ auto d = openDir(path::c_str(mountTarget));
while (auto e = ::readdir(d.get())) {
if (e->d_type == DT_REG) {
auto name = std::string_view(e->d_name);
@@ -874,7 +917,7 @@
if (bindPoints.back().second.dest_path().empty() ||
bindPoints.back().second.source_subdir().empty()) {
bindPoints.pop_back();
- mIncFs->unlink(ifs->control, INCFS_ROOT_INODE, name);
+ mIncFs->unlink(ifs->control, path::join(ifs->root, constants().mount, name));
}
}
} else if (e->d_type == DT_DIR) {
@@ -891,9 +934,7 @@
<< " for mount " << root;
continue;
}
- ifs->storages.insert_or_assign(md.id(),
- IncFsMount::Storage{std::string(name),
- Inode(e->d_ino)});
+ ifs->storages.insert_or_assign(md.id(), IncFsMount::Storage{std::string(name)});
mNextId = std::max(mNextId, md.id() + 1);
}
}
@@ -973,10 +1014,10 @@
}
FileSystemControlParcel fsControlParcel;
fsControlParcel.incremental = std::make_unique<IncrementalFileSystemControlParcel>();
- fsControlParcel.incremental->cmd =
- std::make_unique<os::ParcelFileDescriptor>(base::unique_fd(::dup(ifs.control.cmdFd)));
- fsControlParcel.incremental->log =
- std::make_unique<os::ParcelFileDescriptor>(base::unique_fd(::dup(ifs.control.logFd)));
+ fsControlParcel.incremental->cmd.reset(base::unique_fd(::dup(ifs.control.cmd)));
+ fsControlParcel.incremental->pendingReads.reset(
+ base::unique_fd(::dup(ifs.control.pendingReads)));
+ fsControlParcel.incremental->log.reset(base::unique_fd(::dup(ifs.control.logs)));
sp<IncrementalDataLoaderListener> listener = new IncrementalDataLoaderListener(*this);
bool created = false;
auto status = mIncrementalManager->prepareDataLoader(ifs.mountId, fsControlParcel, *dlp,