10628705eSKadir Cetinkaya //===--- ThreadsafeFS.cpp -------------------------------------------------===//
20628705eSKadir Cetinkaya //
30628705eSKadir Cetinkaya // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40628705eSKadir Cetinkaya // See https://llvm.org/LICENSE.txt for license information.
50628705eSKadir Cetinkaya // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60628705eSKadir Cetinkaya //
70628705eSKadir Cetinkaya //===----------------------------------------------------------------------===//
80628705eSKadir Cetinkaya
90628705eSKadir Cetinkaya #include "support/ThreadsafeFS.h"
100628705eSKadir Cetinkaya #include "Logger.h"
110628705eSKadir Cetinkaya #include "llvm/ADT/SmallString.h"
120628705eSKadir Cetinkaya #include "llvm/ADT/StringRef.h"
130628705eSKadir Cetinkaya #include "llvm/Support/Path.h"
140628705eSKadir Cetinkaya #include "llvm/Support/VirtualFileSystem.h"
150628705eSKadir Cetinkaya #include <memory>
160628705eSKadir Cetinkaya
170628705eSKadir Cetinkaya namespace clang {
180628705eSKadir Cetinkaya namespace clangd {
190628705eSKadir Cetinkaya
200628705eSKadir Cetinkaya namespace {
210628705eSKadir Cetinkaya /// Always opens files in the underlying filesystem as "volatile", meaning they
220628705eSKadir Cetinkaya /// won't be memory-mapped. Memory-mapping isn't desirable for clangd:
230628705eSKadir Cetinkaya /// - edits to the underlying files change contents MemoryBuffers owned by
240628705eSKadir Cetinkaya // SourceManager, breaking its invariants and leading to crashes
250628705eSKadir Cetinkaya /// - it locks files on windows, preventing edits
260628705eSKadir Cetinkaya class VolatileFileSystem : public llvm::vfs::ProxyFileSystem {
270628705eSKadir Cetinkaya public:
VolatileFileSystem(llvm::IntrusiveRefCntPtr<FileSystem> FS)280628705eSKadir Cetinkaya explicit VolatileFileSystem(llvm::IntrusiveRefCntPtr<FileSystem> FS)
290628705eSKadir Cetinkaya : ProxyFileSystem(std::move(FS)) {}
300628705eSKadir Cetinkaya
310628705eSKadir Cetinkaya llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
openFileForRead(const llvm::Twine & InPath)320628705eSKadir Cetinkaya openFileForRead(const llvm::Twine &InPath) override {
330628705eSKadir Cetinkaya llvm::SmallString<128> Path;
340628705eSKadir Cetinkaya InPath.toVector(Path);
350628705eSKadir Cetinkaya
360628705eSKadir Cetinkaya auto File = getUnderlyingFS().openFileForRead(Path);
370628705eSKadir Cetinkaya if (!File)
380628705eSKadir Cetinkaya return File;
390628705eSKadir Cetinkaya // Try to guess preamble files, they can be memory-mapped even on Windows as
400628705eSKadir Cetinkaya // clangd has exclusive access to those and nothing else should touch them.
410628705eSKadir Cetinkaya llvm::StringRef FileName = llvm::sys::path::filename(Path);
42d5953e3eSKazu Hirata if (FileName.starts_with("preamble-") && FileName.ends_with(".pch"))
430628705eSKadir Cetinkaya return File;
44*f7672959SKazu Hirata return std::make_unique<VolatileFile>(std::move(*File));
450628705eSKadir Cetinkaya }
460628705eSKadir Cetinkaya
470628705eSKadir Cetinkaya private:
480628705eSKadir Cetinkaya class VolatileFile : public llvm::vfs::File {
490628705eSKadir Cetinkaya public:
VolatileFile(std::unique_ptr<llvm::vfs::File> Wrapped)500628705eSKadir Cetinkaya VolatileFile(std::unique_ptr<llvm::vfs::File> Wrapped)
510628705eSKadir Cetinkaya : Wrapped(std::move(Wrapped)) {
520628705eSKadir Cetinkaya assert(this->Wrapped);
530628705eSKadir Cetinkaya }
540628705eSKadir Cetinkaya
553f3930a4SKazu Hirata llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
getBuffer(const llvm::Twine & Name,int64_t FileSize,bool RequiresNullTerminator,bool)560628705eSKadir Cetinkaya getBuffer(const llvm::Twine &Name, int64_t FileSize,
570628705eSKadir Cetinkaya bool RequiresNullTerminator, bool /*IsVolatile*/) override {
580628705eSKadir Cetinkaya return Wrapped->getBuffer(Name, FileSize, RequiresNullTerminator,
590628705eSKadir Cetinkaya /*IsVolatile=*/true);
600628705eSKadir Cetinkaya }
610628705eSKadir Cetinkaya
status()620628705eSKadir Cetinkaya llvm::ErrorOr<llvm::vfs::Status> status() override {
630628705eSKadir Cetinkaya return Wrapped->status();
640628705eSKadir Cetinkaya }
getName()650628705eSKadir Cetinkaya llvm::ErrorOr<std::string> getName() override { return Wrapped->getName(); }
close()660628705eSKadir Cetinkaya std::error_code close() override { return Wrapped->close(); }
670628705eSKadir Cetinkaya
680628705eSKadir Cetinkaya private:
690628705eSKadir Cetinkaya std::unique_ptr<File> Wrapped;
700628705eSKadir Cetinkaya };
710628705eSKadir Cetinkaya };
720628705eSKadir Cetinkaya } // namespace
730628705eSKadir Cetinkaya
740628705eSKadir Cetinkaya llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
view(PathRef CWD) const750628705eSKadir Cetinkaya ThreadsafeFS::view(PathRef CWD) const {
76059a23c0SKazu Hirata auto FS = view(std::nullopt);
770628705eSKadir Cetinkaya if (auto EC = FS->setCurrentWorkingDirectory(CWD))
780628705eSKadir Cetinkaya elog("VFS: failed to set CWD to {0}: {1}", CWD, EC.message());
790628705eSKadir Cetinkaya return FS;
800628705eSKadir Cetinkaya }
810628705eSKadir Cetinkaya
820628705eSKadir Cetinkaya llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
viewImpl() const8372568984SSam McCall RealThreadsafeFS::viewImpl() const {
840628705eSKadir Cetinkaya // Avoid using memory-mapped files.
850628705eSKadir Cetinkaya // FIXME: Try to use a similar approach in Sema instead of relying on
860628705eSKadir Cetinkaya // propagation of the 'isVolatile' flag through all layers.
875207f19dSDuncan P. N. Exon Smith return new VolatileFileSystem(llvm::vfs::createPhysicalFileSystem());
880628705eSKadir Cetinkaya }
890628705eSKadir Cetinkaya } // namespace clangd
900628705eSKadir Cetinkaya } // namespace clang
91