[LLVMdev] [RFC] llvm/include/Support/FileOutputBuffer.h
Michael Spencer
bigcheesegs at gmail.com
Fri May 18 15:07:34 PDT 2012
On Thu, May 17, 2012 at 3:25 PM, Nick Kledzik <kledzik at apple.com> wrote:
> I now have an implementation of FileOutputBuffer (OutputBuffer was already taken). The patch supports the functionality listed below and I've tested that it works for lld.
>
> To implement the FileOutputBuffer, I needed to add some more functions to llvm/Support/FileSystem.h, including:
> is_writable_file()
> is_executable_file()
> set_file_executable()
For these parts I would like to follow
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3365.html#TOC
which is what the rest of the fs library is modeled after. So we would
use:
error_code permissions(const Twine &path, perms p);
If the name is confusing (it says nothing about modification) I'm fine
with modify_permissions or something similar.
> unique_file_sized()
Instead of adding this I would much rather add resize_file and call it
after unique_file.
> map_file_pages()
> unmap_file_pages()
These are good.
> I've implemented the unix side for all these, but have the windows side just stubbed out.
>
> I've also added a test case (unittests/Support/FileOutputBufferTest.cpp) which exercises FileOutputBuffer.
>
> -Nick
As a generally comment there is lots of trailing white space and tabs
and white space only changes.
Inline comments:
> Index: include/llvm/Support/FileSystem.h
> ===================================================================
> --- include/llvm/Support/FileSystem.h (revision 157010)
> +++ include/llvm/Support/FileSystem.h (working copy)
> @@ -426,12 +454,35 @@
> /// @param result_path Set to the opened file's absolute path.
> /// @param makeAbsolute If true and @model is not an absolute path, a temp
> /// directory will be prepended.
> +/// @param private_file If true file will have permissions set to only
> +/// be accessible by the current user. If false, then the file
> +/// permissions are set to be globally readable.
> /// @results errc::success if result_{fd,path} have been successfully set,
> /// otherwise a platform specific error_code.
> error_code unique_file(const Twine &model, int &result_fd,
> - SmallVectorImpl<char> &result_path,
> - bool makeAbsolute = true, unsigned mode = 0600);
> + SmallVectorImpl<char> &result_path,
> + bool makeAbsolute = true,
> + bool private_file = true);
Incorrect whitespace only change and tabs.
> Index: include/llvm/Support/FileOutputBuffer.h
> ===================================================================
> --- include/llvm/Support/FileOutputBuffer.h (revision 0)
> +++ include/llvm/Support/FileOutputBuffer.h (revision 0)
> @@ -0,0 +1,97 @@
> +//=== FileOutputBuffer.h - File Output Buffer -------------------*- C++ -*-===//
> +//
> +// The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// Utility for creating a in-memory buffer that will be written to a file.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_SUPPORT_FILEOUTPUTBUFFER_H
> +#define LLVM_SUPPORT_FILEOUTPUTBUFFER_H
> +
> +#include "llvm/Support/DataTypes.h"
> +#include "llvm/ADT/StringRef.h"
> +#include "llvm/ADT/SmallString.h"
> +
> +namespace llvm {
> +
> +class error_code;
> +template<class T> class OwningPtr;
> +
> +/// FileOutputBuffer - This interface provides simple way to create an in-memory
> +/// buffer which will be written to a file. During the lifetime of these
> +/// objects, the content or existence of the specified file is undefined. That
> +/// is, creating an OutputBuffer for a file may immediately remove the file.
> +/// If the FileOutputBuffer is committed, the target file's content will become
> +/// the buffer content at the time of the commit. If the FileOutputBuffer is
> +/// not committed, the file will be deleted in the FileOutputBuffer destructor.
> +class FileOutputBuffer {
> +public:
> +
> + enum {
> + F_executable = 1 /// set the 'x' bit on the resulting file
> + };
> +
> + /// Factory method to create an OutputBuffer object which manages a read/write
> + /// buffer of the specified size. When committed, the buffer will be written
> + /// to the file at the specified path.
> + static error_code create(StringRef filePath, size_t size,
> + OwningPtr<FileOutputBuffer> &result,
> + unsigned flags=0);
Arguments should start with an upper case letter. (same for all of these)
> + /// Returns a pointer to the start of the buffer.
> + uint8_t *getBufferStart() const {
> + return bufferStart;
> + }
> +
> + /// Returns a pointer to the end of the buffer.
> + uint8_t *getBufferEnd() const {
> + return bufferEnd;
> + }
> +
> + /// Returns size of the buffer.
> + size_t getBufferSize() const {
> + return bufferEnd - bufferStart;
> + }
> +
> + /// Returns path where file will show up if buffer is committed.
> + StringRef getPath() const {
> + return finalPath;
> + }
> +
> + /// Flushes the content of the buffer to its file and deallocates the
> + /// buffer. If commit() is not called before this object's destructor
> + /// is called, the file is deleted in the destructor. The optional parameter
> + /// is used if it turns out you want the file size to be smaller than
> + /// initially requested.
> + virtual error_code commit(int64_t newSmallerSize = -1);
Why is this virtual? I don't ever see a need to subclass FileOutputBuffer.
> + /// If this object was previously committed, the destructor just deletes
> + /// this object. If this object was not committed, the destructor
> + /// deallocates the buffer and the target file is never written.
> + virtual ~FileOutputBuffer();
Same.
> +
> +
> +protected:
> + FileOutputBuffer(const FileOutputBuffer &); // DO NOT IMPLEMENT
> + FileOutputBuffer &operator=(const FileOutputBuffer &); // DO NOT IMPLEMENT
> + FileOutputBuffer(uint8_t *start, uint8_t *end,
> + StringRef path, StringRef tempPath);
> +
> + uint8_t *bufferStart;
> + uint8_t *bufferEnd;
> + SmallString<128> finalPath;
> + SmallString<128> tempPath;
These should all start with an uppercase letter.
> +};
> +
> +
> +
> +} // end namespace llvm
> +
> +#endif
> Index: unittests/Support/FileOutputBufferTest.cpp
> ===================================================================
> --- unittests/Support/FileOutputBufferTest.cpp (revision 0)
> +++ unittests/Support/FileOutputBufferTest.cpp (revision 0)
> @@ -0,0 +1,131 @@
> +//===- llvm/unittest/Support/FileOutputBuffer.cpp - unit tests ------------===//
> +//
> +// The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "llvm/Support/FileSystem.h"
> +#include "llvm/Support/PathV2.h"
> +#include "llvm/Support/ErrorHandling.h"
> +#include "llvm/Support/raw_ostream.h"
> +#include "llvm/Support/FileOutputBuffer.h"
> +#include "llvm/ADT/OwningPtr.h"
> +
> +#include "gtest/gtest.h"
> +
> +using namespace llvm;
> +using namespace llvm::sys;
> +
> +#define ASSERT_NO_ERROR(x) \
> + if (error_code ASSERT_NO_ERROR_ec = x) { \
> + errs() << #x ": did not return errc::success.\n" \
> + << "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \
> + << "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \
> + } else {}
> +
> +namespace {
> +
> +
> +TEST(FileOutputBuffer, Test) {
> + // Create unique temporary directory for these tests
> + SmallString<128> TestDirectory;
> + {
> + int fd;
> + ASSERT_NO_ERROR(
> + fs::unique_file("FileOutputBuffer-test-%%-%%-%%-%%/dir", fd,
> + TestDirectory));
> + ::close(fd);
> + TestDirectory = path::parent_path(TestDirectory);
> + }
> +
> + // TEST 1: Verify commit case.
> + SmallString<128> File1(TestDirectory);
> + File1.append("/file1");
> + {
> + OwningPtr<FileOutputBuffer> Buffer;
> + ASSERT_NO_ERROR(FileOutputBuffer::create(File1, 8192, Buffer));
> + // Start buffer with special header.
> + memcpy(Buffer->getBufferStart(), "AABBCCDDEEFFGGHHIIJJ", 20);
> + // Write to end of buffer to verify it is writable.
> + memcpy(Buffer->getBufferEnd() - 20, "AABBCCDDEEFFGGHHIIJJ", 20);
> + // Commit buffer.
> + ASSERT_NO_ERROR(Buffer->commit());
> + }
> + // Verify file exists and starts with special header.
> + bool MagicMatches = false;
> + ASSERT_NO_ERROR(fs::has_magic(Twine(File1), Twine("AABBCCDDEEFFGGHHIIJJ"),
> + MagicMatches));
> + EXPECT_TRUE(MagicMatches);
> + // Verify file is correct size.
> + uint64_t File1Size;
> + ASSERT_NO_ERROR(fs::file_size(Twine(File1), File1Size));
> + ASSERT_EQ(File1Size, 8192ULL);
> +
> + // TEST 2: Verify abort case.
> + SmallString<128> File2(TestDirectory);
> + File2.append("/file2");
> + {
> + OwningPtr<FileOutputBuffer> Buffer2;
> + ASSERT_NO_ERROR(FileOutputBuffer::create(File2, 8192, Buffer2));
> + // Fill buffer with special header.
> + memcpy(Buffer2->getBufferStart(), "AABBCCDDEEFFGGHHIIJJ", 20);
> + // Do *not* commit buffer.
> + }
> + // Verify file does not exist (because buffer not commited).
> + bool Exists = false;
> + ASSERT_NO_ERROR(fs::exists(Twine(File2), Exists));
> + EXPECT_FALSE(Exists);
> +
> +
> + // TEST 3: Verify sizing down case.
> + SmallString<128> File3(TestDirectory);
> + File3.append("/file3");
> + {
> + OwningPtr<FileOutputBuffer> Buffer;
> + ASSERT_NO_ERROR(FileOutputBuffer::create(File3, 8192000, Buffer));
> + // Start buffer with special header.
> + memcpy(Buffer->getBufferStart(), "AABBCCDDEEFFGGHHIIJJ", 20);
> + // Write to end of buffer to verify it is writable.
> + memcpy(Buffer->getBufferEnd() - 20, "AABBCCDDEEFFGGHHIIJJ", 20);
> + // Commit buffer, but size down to smaller size
> + ASSERT_NO_ERROR(Buffer->commit(5000));
> + }
> + // Verify file exists and starts with special header.
> + bool MagicMatches3 = false;
> + ASSERT_NO_ERROR(fs::has_magic(Twine(File3), Twine("AABBCCDDEEFFGGHHIIJJ"),
> + MagicMatches3));
> + EXPECT_TRUE(MagicMatches3);
> + // Verify file is correct size.
> + uint64_t File3Size;
> + ASSERT_NO_ERROR(fs::file_size(Twine(File3), File3Size));
> + ASSERT_EQ(File3Size, 5000ULL);
> +
> +
> + // TEST 4: Verify file can be made executable.
> + SmallString<128> File4(TestDirectory);
> + File4.append("/file4");
> + {
> + OwningPtr<FileOutputBuffer> Buffer;
> + ASSERT_NO_ERROR(FileOutputBuffer::create(File4, 8192, Buffer,
> + FileOutputBuffer::F_executable));
> + // Start buffer with special header.
> + memcpy(Buffer->getBufferStart(), "AABBCCDDEEFFGGHHIIJJ", 20);
> + // Commit buffer.
> + ASSERT_NO_ERROR(Buffer->commit());
> + }
> + // Verify file exists and is executable.
> + bool IsExecutable;
> + ASSERT_NO_ERROR(fs::is_executable_file(Twine(File4), IsExecutable));
> + EXPECT_TRUE(IsExecutable);
> +
> +
> + // Clean up.
> + uint32_t RemovedCount;
> + ASSERT_NO_ERROR(fs::remove_all(TestDirectory.str(), RemovedCount));
> +}
> +
> +
> +} // anonymous namespace
> Index: unittests/CMakeLists.txt
> ===================================================================
> --- unittests/CMakeLists.txt (revision 157010)
> +++ unittests/CMakeLists.txt (working copy)
> @@ -165,6 +165,7 @@
> Support/CommandLineTest.cpp
> Support/ConstantRangeTest.cpp
> Support/EndianTest.cpp
> + Support/FileOutputBufferTest.cpp
> Support/LeakDetectorTest.cpp
> Support/MathExtrasTest.cpp
> Support/Path.cpp
> Index: lib/Support/Unix/PathV2.inc
> ===================================================================
> --- lib/Support/Unix/PathV2.inc (revision 157010)
> +++ lib/Support/Unix/PathV2.inc (working copy)
> @@ -24,6 +24,9 @@
> #if HAVE_FCNTL_H
> #include <fcntl.h>
> #endif
> +#ifdef HAVE_SYS_MMAN_H
> +#include <sys/mman.h>
> +#endif
> #if HAVE_DIRENT_H
> # include <dirent.h>
> # define NAMLEN(dirent) strlen((dirent)->d_name)
> @@ -52,6 +55,7 @@
> # define PATH_MAX 4096
> #endif
>
> +
> using namespace llvm;
>
> namespace {
> @@ -347,10 +351,19 @@
> return error_code::success();
> }
>
> -// Since this is most often used for temporary files, mode defaults to 0600.
> +static mode_t current_umask() {
> + // There is no posix way to just get current umask. You
> + // have to set it to something else, and get the "old" value.
> + // So, get and reset to get the current umask value;
> + mode_t mask = ::umask(0);
> + ::umask(mask);
> + return mask;
> +}
> +
> +
> error_code unique_file(const Twine &model, int &result_fd,
> - SmallVectorImpl<char> &result_path,
> - bool makeAbsolute, unsigned mode) {
> + SmallVectorImpl<char> &result_path,
> + bool makeAbsolute, bool private_file) {
Incorrect whitespace change.
> SmallString<128> Model;
> model.toVector(Model);
> // Null terminate.
> @@ -380,7 +393,8 @@
>
> // Try to open + create the file.
> rety_open_create:
> - int RandomFD = ::open(RandomPath.c_str(), O_RDWR | O_CREAT | O_EXCL, mode);
> + int perms = (private_file ? 0600 : 0666) & ~(current_umask());
> + int RandomFD = ::open(RandomPath.c_str(), O_RDWR | O_CREAT | O_EXCL, perms);
> if (RandomFD == -1) {
> // If the file existed, try again, otherwise, error.
> if (errno == errc::file_exists)
> @@ -426,6 +440,28 @@
> return error_code::success();
> }
>
> +
> +error_code unique_file_sized(const Twine &model, size_t size,
> + SmallVectorImpl<char> &result_path,
> + bool makeAbsolute) {
> + int fd;
> + if ( error_code ec = unique_file(model, fd, result_path, makeAbsolute, false) )
> + return ec;
> +
> + if ( ::ftruncate(fd, size) == -1 ) {
> + int error = errno;
> + result_path.push_back('\0');
This is incorrect because result_path now actually includes a \0. I added a
function c_str(container) specifically for this case that adds a 0 then removes
it.
> + ::unlink(result_path.begin());
> + ::close(fd);
> + return error_code(error, system_category());
> + }
> +
> + ::close(fd);
> +
> + return error_code::success();
> +}
> +
> +
> error_code detail::directory_iterator_construct(detail::DirIterState &it,
> StringRef path){
> SmallString<128> path_null(path);
> @@ -496,6 +532,97 @@
> return error_code::success();
> }
>
> +error_code map_file_pages(const Twine &path, off_t file_offset, size_t size,
> + bool map_writable, void *&result) {
> + SmallString<128> path_storage;
> + StringRef name = path.toNullTerminatedStringRef(path_storage);
> + int oflags = map_writable ? O_RDWR : O_RDONLY;
> + int fd = ::open(name.begin(), oflags);
> + if ( fd == -1 )
> + return error_code(errno, system_category());
> + int flags = map_writable ? MAP_SHARED : MAP_PRIVATE;
> + int prot = map_writable ? (PROT_READ|PROT_WRITE) : PROT_READ;
> +#ifdef MAP_FILE
> + flags |= MAP_FILE;
> +#endif
> + result = ::mmap(0, size, prot, flags, fd, file_offset);
> + if (result == MAP_FAILED) {
> + ::close(fd);
> + return error_code(errno, system_category());
> + }
> +
> + ::close(fd);
Could AutoFD work here?
> + return error_code::success();
> +}
> +
> +error_code unmap_file_pages(void *base, size_t size) {
> + if ( ::munmap(base, size) == -1 )
> + return error_code(errno, system_category());
> +
> + return error_code::success();
> +}
> +
> +
> +error_code is_writable_file(const Twine &path, bool &result) {
> + result = false;
> + SmallString<128> path_storage;
> + StringRef name = path.toNullTerminatedStringRef(path_storage);
> + if ( ::access(name.begin(), W_OK) == -1 ) {
> + int error = errno;
> + if ( error == EACCES )
> + result = false;
> + else
> + return error_code(error, system_category());
> + } else {
> + result = true;
> + }
> + return error_code::success();
> +}
> +
> +
> +error_code is_executable_file(const Twine &path, bool &result) {
> + result = false;
> + SmallString<128> path_storage;
> + StringRef name = path.toNullTerminatedStringRef(path_storage);
> + if ( ::access(name.begin(), X_OK) == -1 ) {
> + int error = errno;
> + if ( error == EACCES )
> + result = false;
> + else
> + return error_code(error, system_category());
> + } else {
> + result = true;
> + }
> + return error_code::success();
> +}
> +
> +
> +
> +error_code set_file_executable(const Twine &path) {
> + SmallString<128> path_storage;
> + StringRef name = path.toNullTerminatedStringRef(path_storage);
> +
> + struct stat buf;
> + if ( stat(name.begin(), &buf) == -1 )
> + return error_code(errno, system_category());
> +
> + // Only add 'x' bit where 'r' bit is already set
> + mode_t perms = buf.st_mode;
> + if ( perms & S_IRUSR )
> + perms |= S_IXUSR;
> + if ( perms & S_IRGRP )
> + perms |= S_IXGRP;
> + if ( perms & S_IROTH )
> + perms |= S_IXOTH;
> +
> + if ( ::chmod(name.begin(), perms) == -1)
> + return error_code(errno, system_category());
> +
> + return error_code::success();
> +}
> +
> +
> +
> } // end namespace fs
> } // end namespace sys
> } // end namespace llvm
> Index: lib/Support/FileOutputBuffer.cpp
> ===================================================================
> --- lib/Support/FileOutputBuffer.cpp (revision 0)
> +++ lib/Support/FileOutputBuffer.cpp (revision 0)
> @@ -0,0 +1,127 @@
> +//===- FileOutputBuffer.cpp - File Output Buffer ----------------*- C++ -*-===//
> +//
> +// The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// Utility for creating a in-memory buffer that will be written to a file.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "llvm/Support/FileOutputBuffer.h"
> +#include "llvm/Support/system_error.h"
> +#include "llvm/Support/FileSystem.h"
> +
> +#include "llvm/Support/raw_ostream.h"
> +
> +#include "llvm/ADT/SmallVector.h"
> +#include "llvm/ADT/OwningPtr.h"
These should be sorted alphabetically except for the main header.
> +
> +namespace llvm {
> +
> +
> +FileOutputBuffer::FileOutputBuffer(uint8_t *start, uint8_t *end,
> + StringRef path, StringRef tmpPath)
Missing an alignment space.
> + : bufferStart(start), bufferEnd(end) {
> + finalPath.assign(path);
> + tempPath.assign(tmpPath);
> +}
> +
> +
> +FileOutputBuffer::~FileOutputBuffer() {
> + // If not already commited, delete buffer and remove temp file.
> + if ( bufferStart != NULL ) {
> + sys::fs::unmap_file_pages((void*)bufferStart, getBufferSize());
> + bool existed;
> + sys::fs::remove(Twine(tempPath), existed);
> + }
> +}
> +
> +
> +
> +error_code FileOutputBuffer::create(StringRef filePath,
> + size_t size,
> + OwningPtr<FileOutputBuffer> &result,
> + unsigned flags) {
Alignment whitespace and capitalized first character.
> + // If file already exists, it must be a regular file (to be mappable).
> + Twine filePathTwine(filePath);
Twine should not be used like this. It stores references to temporaries. It will
work in this case because filePath is not a temporary and you haven't invoked
any operators. It's also uneeded. StringRef is convertable to Twine.
> + sys::fs::file_status stat;
> + bool writable;
This is only used in the case below, so it should be folded in there.
> + error_code ec = sys::fs::status(filePathTwine, stat);
stat is undefined if ec isn't success. ec will be success even in the case of
file_not_found.
> + switch ( stat.type() ) {
No space after ( and before ). Same lots of other places.
> + case sys::fs::file_type::file_not_found:
> + // If file does not exist, we'll create one.
> + break;
> + case sys::fs::file_type::regular_file:
> + // If file is not currently writable, error out.
> + ec = sys::fs::is_writable_file(filePathTwine, writable);
> + if ( !ec && !writable )
> + return make_error_code(errc::permission_denied);
> + break;
> + default:
> + if ( ec )
> + return ec;
> + else
> + return make_error_code(errc::operation_not_permitted);
> + }
> +
> + // Delete target file.
> + bool existed;
> + if ( error_code ec = sys::fs::remove(filePath, existed) )
> + return ec;
> +
> + // Create new file of specified size in same directory but with random name.
> + SmallString<128> modelStr;
> + modelStr.assign(filePath);
> + modelStr.append(".tmp%%%%%%%");
> + SmallString<128> tempFilePath;
> + if ( error_code ec = sys::fs::unique_file_sized(Twine(modelStr), size,
> + tempFilePath, false) ) {
This should be: Twine(filePath) + ".tmp%%%%%%%"
> + return ec;
> + }
Unneeded block.
> +
> + // If requested, make the output file executable.
> + if ( flags & F_executable )
Tab.
> + sys::fs::set_file_executable(Twine(tempFilePath));
> +
> + // Memory map new file.
> + void *base;
> + if ( error_code ec = sys::fs::map_file_pages(Twine(tempFilePath),
> + 0, size, true, base) ) {
> + return ec;
> + }
> +
> + // Create FileOutputBuffer object to own mapped range.
> + uint8_t* start = (uint8_t*)base;
* should be right aligned and the case should be a c++ cast.
> + result.reset(new FileOutputBuffer(start, start+size, filePath, tempFilePath));
Spaces around binary operator +.
> +
> + return error_code::success();
> +}
> +
> +
> +error_code FileOutputBuffer::commit(int64_t newSmallerSize) {
> + // Unmap buffer, letting OS flush dirty pages to file on disk.
> + if ( error_code ec = sys::fs::unmap_file_pages((void*)bufferStart,
c++ cast.
> + getBufferSize()) ) {
> + return ec;
> + }
> +
> + // If requested, resize file as part of commit.
> + if ( newSmallerSize != -1 ) {
> + if ( error_code ec = sys::fs::resize_file(Twine(tempPath), newSmallerSize) )
> + return ec;
> + }
> +
> + // Rename file to final name.
> + if ( error_code ec = sys::fs::rename(Twine(tempPath), Twine(finalPath)) )
> + return ec;
> +
> + return error_code::success();
> +}
> +
> +
> +} // namespace
> +
> Index: lib/Support/CMakeLists.txt
> ===================================================================
> --- lib/Support/CMakeLists.txt (revision 157010)
> +++ lib/Support/CMakeLists.txt (working copy)
> @@ -23,6 +23,7 @@
> Dwarf.cpp
> ErrorHandling.cpp
> FileUtilities.cpp
> + FileOutputBuffer.cpp
> FoldingSet.cpp
> FormattedStream.cpp
> GraphWriter.cpp
> Index: lib/Support/Windows/PathV2.inc
> ===================================================================
> --- lib/Support/Windows/PathV2.inc (revision 157010)
> +++ lib/Support/Windows/PathV2.inc (working copy)
> @@ -501,7 +501,7 @@
> // it currently comes in as a UNIX mode.
> error_code unique_file(const Twine &model, int &result_fd,
> SmallVectorImpl<char> &result_path,
> - bool makeAbsolute, unsigned mode) {
> + bool makeAbsolute, bool private_file) {
> // Use result_path as temp storage.
> result_path.set_size(0);
> StringRef m = model.toStringRef(result_path);
> @@ -755,6 +755,43 @@
> return error_code::success();
> }
>
> +error_code map_file_pages(const Twine &path, off_t file_offset, size_t size,
> + bool map_writable, void *&result) {
> + assert(0 && "NOT IMPLEMENTED");
> +}
> +
> +error_code unmap_file_pages(void *base, size_t size) {
> + assert(0 && "NOT IMPLEMENTED");
> +}
> +
> +error_code is_writable_file(const Twine &path, bool &result) {
> +#if 0 // verify code below before enabling:
> + SmallString<128> path_storage;
> + StringRef name = path.toNullTerminatedStringRef(path_storage);
> +
> + DWORD attr = GetFileAttributes(name.begin());
> +
> + if (attr == INVALID_FILE_ATTRIBUTES)
> + return windows_error(::GetLastError());
> +
> + // Files are writable by default, unless marked READONLY
> + result = !(attr & FILE_ATTRIBUTE_READONLY);
> + #endif
This looks right, the only thing it's missing is symlink handling.
> + return error_code::success();
> +}
> +
> +error_code is_executable_file(const Twine &path, bool &result) {
> + // All files are executable on Windows (ignoring security attributes).
> + result = true;
> + return error_code::success();
> +}
> +
> +error_code set_file_executable(const Twine &path) {
> + // All files are executable on Windows (ignoring security attributes).
> + return error_code::success();
> +}
> +
> +
> } // end namespace fs
> } // end namespace sys
> } // end namespace llvm
>
More information about the llvm-dev
mailing list