fsck: extract orphaned symlink inodes as symlinks

Summary:
Update the fsck code to save any orphaned symlink inodes that it finds as
symlinks in the repair archive directory, rather than saving the contents as a
regular file.

Reviewed By: wez

Differential Revision: D17170346

fbshipit-source-id: 4cba8b27233b728114a80a327ab519b039297aea
This commit is contained in:
Adam Simpkins 2019-09-04 11:05:47 -07:00 committed by Facebook Github Bot
parent 3f7e72dc3e
commit 92b770a40e
2 changed files with 45 additions and 15 deletions

View File

@ -7,7 +7,9 @@
#include "eden/fs/inodes/overlay/OverlayChecker.h"
#include <boost/filesystem.hpp>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <folly/Conv.h>
#include <folly/ExceptionWrapper.h>
@ -489,16 +491,51 @@ class OverlayChecker::OrphanInode : public OverlayChecker::Error {
RepairState& repair,
InodeNumber number,
AbsolutePath archivePath,
mode_t /* mode */) const {
mode_t mode) const {
auto input =
repair.fs()->openFile(number, FsOverlay::kHeaderIdentifierFile);
folly::File output(
archivePath.value(), O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
// TODO: if the mode parameter indicates that this file is a symlink,
// extract it as a symlink rather than a regular file.
// If the file is a symlink, try to create the file in the archive
// directory as a symlink.
if (S_ISLNK(mode)) {
// The maximum symlink size on Linux is really filesystem dependent.
// _POSIX_SYMLINK_MAX is typically defined as 255, but various filesystems
// have larger limits. In practice ext4, btrfs, and tmpfs appear to limit
// symlinks to 4095 bytes. xfs appears to have a limit of 1023 bytes.
//
// Try reading up to 4096 bytes here. If the data is longer than this, or
// if we get ENAMETOOLONG when creating the symlink, we fall back and
// extract the data as a regular file.
constexpr size_t maxLength = 4096;
std::vector<char> contents(maxLength);
auto bytesRead = folly::preadFull(
input.fd(),
contents.data(),
contents.size(),
FsOverlay::kHeaderLength);
if (bytesRead < 0) {
folly::throwSystemError(
"read error while copying symlink data from inode ",
number,
" to ",
archivePath);
}
if (0 < bytesRead && static_cast<size_t>(bytesRead) < maxLength) {
auto rc = ::symlink(contents.data(), archivePath.value().c_str());
if (rc == 0) {
// We successfully created a symlink of the contents, so we're done.
return;
}
}
// If we can't save the contents as a symlink, fall through and just
// save them as a regular file. We used pread() above, so the input file
// position will still be at the start of the data, and we don't need to
// reset it.
}
// Copy the data
folly::File output(
archivePath.value(), O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0600);
size_t blockSize = 1024 * 1024;
std::vector<uint8_t> buffer;
buffer.resize(blockSize);

View File

@ -303,16 +303,9 @@ class SnapshotTestBase(
# files and symlinks, so sockets do not get extracted.
continue
elif expected_file.file_type == stat_mod.S_IFLNK:
if new_fsck:
# At the moment the C++ fsck code extracts symlinks as regular
# files
expected.add_file(
orphan_path, expected_file.contents, perms=0o600
)
else:
expected.add_symlink(
orphan_path, expected_file.contents, perms=0o777
)
expected.add_symlink(
orphan_path, expected_file.contents, perms=0o777
)
elif expected_file.file_type == stat_mod.S_IFREG:
expected.add_file(orphan_path, expected_file.contents, perms=0o600)
else: