Skip to content

Commit 7e7d2a9

Browse files
MichaelCuevasmeta-codesync[bot]
authored andcommitted
PrivHelperServer: Share mount parsing logic using setmntent
Summary: Refactor mount table parsing to use a shared `forEachMountEntry` helper function that uses setmntent/getmntent to parse /proc/mounts. This allows both `isOldEdenMount` (used by detectAndUnmountStaleMount) and `cleanupStaleBindMounts` to share the same mount parsing logic. The macOS path for isOldEdenMount continues to use getmntinfo as before since setmntent is Linux-only. Reviewed By: kavehahmadi60 Differential Revision: D95326706 fbshipit-source-id: 5ca29579c6d495de3fde9c9f0d4983da5ab2373a
1 parent 62164c9 commit 7e7d2a9

1 file changed

Lines changed: 70 additions & 58 deletions

File tree

eden/fs/privhelper/PrivHelperServerSanityCheck.cpp

Lines changed: 70 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -34,54 +34,73 @@ namespace facebook::eden {
3434

3535
namespace {
3636

37-
bool getSystemMountList(std::string& out) {
38-
#ifdef __APPLE__
39-
struct statfs* buf;
40-
int count = getmntinfo(&buf, MNT_WAIT);
41-
if (count == 0) {
42-
XLOGF(ERR, "getmntinfo failed: {}", folly::errnoStr(errno));
37+
#ifdef __linux__
38+
/**
39+
* Callback type for iterating over mount entries.
40+
* Return true to continue iteration, false to stop early.
41+
*/
42+
using MountEntryCallback =
43+
std::function<bool(const std::string& fsName, const std::string& mountDir)>;
44+
45+
/**
46+
* Iterates over the system mount table using setmntent/getmntent.
47+
* Calls the provided callback for each mount entry.
48+
* Returns false if we failed to open the mount table.
49+
*/
50+
bool forEachMountEntry(const MountEntryCallback& callback) {
51+
FILE* mtab = setmntent("/proc/mounts", "r");
52+
if (mtab == nullptr) {
53+
XLOGF(WARN, "Failed to open /proc/mounts: {}", folly::errnoStr(errno));
4354
return false;
4455
}
45-
for (int i = 0; i < count; i++) {
46-
out += fmt::format(
47-
"{} {} {}\n",
48-
buf[i].f_mntfromname,
49-
buf[i].f_mntonname,
50-
buf[i].f_fstypename);
56+
SCOPE_EXIT {
57+
endmntent(mtab);
58+
};
59+
60+
struct mntent* entry;
61+
while ((entry = getmntent(mtab)) != nullptr) {
62+
if (!callback(entry->mnt_fsname, entry->mnt_dir)) {
63+
break;
64+
}
5165
}
5266
return true;
53-
#else
54-
if (folly::readFile("/proc/mounts", out)) {
55-
return true;
56-
} else {
57-
XLOGF(ERR, "failed to read /proc/mounts: {}", folly::errnoStr(errno));
58-
return false;
59-
}
60-
#endif
6167
}
68+
#endif
6269

63-
/* Determines whether the given mountPoint is contained in the mount table
70+
/**
71+
* Determines whether the given mountPoint is contained in the mount table
6472
* and looks like it was previously mounted by EdenFS.
6573
*/
6674
bool isOldEdenMount(const std::string& mountPoint) {
67-
std::string mounts;
68-
if (getSystemMountList(mounts)) {
69-
// TODO(T201411922): Update to std::string_view once our macOS build uses
70-
// C++20.
71-
// https://en.cppreference.com/w/cpp/string/basic_string_view/starts_with
72-
std::vector<folly::StringPiece> lines;
73-
folly::split('\n', mounts, lines);
74-
75-
for (const auto& line : lines) {
76-
// We expect EdenFS mounts to look like the following:
77-
// edenfs: {mountPoint} fuse ...
78-
if (is_edenfs_fs_mount(line, mountPoint)) {
75+
#ifdef __linux__
76+
bool found = false;
77+
bool success = forEachMountEntry(
78+
[&mountPoint, &found](
79+
const std::string& fsName, const std::string& mountDir) {
80+
if (mountDir == mountPoint && is_edenfs_fs_type(fsName)) {
81+
found = true;
82+
return false;
83+
}
84+
return true;
85+
});
86+
87+
if (success && found) {
88+
return true;
89+
}
90+
#else
91+
struct statfs* buf;
92+
int count = getmntinfo(&buf, MNT_WAIT);
93+
if (count == 0) {
94+
XLOGF(ERR, "getmntinfo failed: {}", folly::errnoStr(errno));
95+
} else {
96+
for (int i = 0; i < count; i++) {
97+
if (std::string(buf[i].f_mntonname) == mountPoint &&
98+
is_edenfs_fs_type(buf[i].f_fstypename)) {
7999
return true;
80100
}
81101
}
82102
}
83-
// We couldn't verify that the mount is an old, disconnected EdenFS mount.
84-
// We assume it isn't to be safe.
103+
#endif
85104
XLOGF(DBG4, "Could not verify that {} is an old EdenFS mount.", mountPoint);
86105
return false;
87106
}
@@ -171,33 +190,26 @@ void PrivHelperServer::cleanupStaleBindMounts(const std::string& checkoutPath) {
171190
// Parse /proc/mounts to find stale redirection mounts under this checkout.
172191
// These are mounts that EdenFS set up (e.g., buck-out) that were left behind
173192
// when EdenFS crashed without properly unmounting.
174-
FILE* mtab = setmntent("/proc/mounts", "r");
175-
if (mtab == nullptr) {
176-
XLOGF(WARN, "Failed to open /proc/mounts, skipping redirection cleanup");
177-
return;
178-
}
179-
SCOPE_EXIT {
180-
endmntent(mtab);
181-
};
182-
183193
std::vector<std::string> staleMounts;
184194
std::string checkoutPrefix = checkoutPath + "/";
185195

186-
struct mntent* entry;
187-
while ((entry = getmntent(mtab)) != nullptr) {
188-
std::string mountPoint = entry->mnt_dir;
189-
190-
if (!folly::StringPiece(mountPoint).startsWith(checkoutPrefix)) {
191-
continue;
192-
}
196+
bool success = forEachMountEntry(
197+
[&checkoutPrefix, &checkoutPath, &staleMounts](
198+
const std::string& /*fsName*/, const std::string& mountDir) {
199+
if (folly::StringPiece(mountDir).startsWith(checkoutPrefix)) {
200+
XLOGF(
201+
INFO,
202+
"Found potential stale redirection mount under {}: {}",
203+
checkoutPath,
204+
mountDir);
205+
staleMounts.push_back(mountDir);
206+
}
207+
return true;
208+
});
193209

194-
// This is a mount under our checkout (likely an EdenFS redirection)
195-
XLOGF(
196-
INFO,
197-
"Found potential stale redirection mount under {}: {}",
198-
checkoutPath,
199-
mountPoint);
200-
staleMounts.push_back(std::move(mountPoint));
210+
if (!success) {
211+
XLOGF(WARN, "Skipping redirection cleanup due to mount table read failure");
212+
return;
201213
}
202214

203215
// Unmount any stale mounts we found (in reverse order to handle nested

0 commit comments

Comments
 (0)