diff options
-rw-r--r-- | include/llvm/Support/FileSystem.h | 94 | ||||
-rw-r--r-- | lib/Support/Unix/PathV2.inc | 78 | ||||
-rw-r--r-- | lib/Support/Windows/PathV2.inc | 46 | ||||
-rw-r--r-- | unittests/Support/Path.cpp | 65 |
4 files changed, 274 insertions, 9 deletions
diff --git a/include/llvm/Support/FileSystem.h b/include/llvm/Support/FileSystem.h index 4bee205..4eb75c4 100644 --- a/include/llvm/Support/FileSystem.h +++ b/include/llvm/Support/FileSystem.h @@ -94,6 +94,55 @@ struct space_info { uint64_t available; }; + +enum perms { + no_perms = 0, + owner_read = 0400, + owner_write = 0200, + owner_exe = 0100, + owner_all = owner_read | owner_write | owner_exe, + group_read = 040, + group_write = 020, + group_exe = 010, + group_all = group_read | group_write | group_exe, + others_read = 04, + others_write = 02, + others_exe = 01, + others_all = others_read | others_write | others_exe, + all_all = owner_all | group_all | others_all, + set_uid_on_exe = 04000, + set_gid_on_exe = 02000, + sticky_bit = 01000, + perms_mask = all_all | set_uid_on_exe | set_gid_on_exe | sticky_bit, + perms_not_known = 0xFFFF, + add_perms = 0x1000, + remove_perms = 0x2000, + symlink_perms = 0x4000 +}; + +// Helper functions so that you can use & and | to manipulate perms bits: +inline perms operator|(perms l , perms r) { + return static_cast<perms>( + static_cast<unsigned short>(l) | static_cast<unsigned short>(r)); +} +inline perms operator&(perms l , perms r) { + return static_cast<perms>( + static_cast<unsigned short>(l) & static_cast<unsigned short>(r)); +} +inline perms &operator|=(perms &l, perms r) { + l = l | r; + return l; +} +inline perms &operator&=(perms &l, perms r) { + l = l & r; + return l; +} +inline perms operator~(perms x) { + return static_cast<perms>(~static_cast<unsigned short>(x)); +} + + + /// file_status - Represents the result of a call to stat and friends. It has /// a platform specific member to store the result. class file_status @@ -113,12 +162,19 @@ class file_status friend bool equivalent(file_status A, file_status B); friend error_code status(const Twine &path, file_status &result); file_type Type; + perms Perms; public: - explicit file_status(file_type v=file_type::status_error) - : Type(v) {} + explicit file_status(file_type v=file_type::status_error, + perms prms=perms_not_known) + : Type(v), Perms(prms) {} + // getters file_type type() const { return Type; } + perms permissions() const { return Perms; } + + // setters void type(file_type v) { Type = v; } + void permissions(perms p) { Perms = p; } }; /// file_magic - An "enum class" enumeration of file types based on magic (the first @@ -395,6 +451,13 @@ error_code is_symlink(const Twine &path, bool &result); /// platform specific error_code. error_code status(const Twine &path, file_status &result); +/// @brief Modifies permission bits on a file +/// +/// @param path Input path. +/// @results errc::success if permissions have been changed, otherwise a +/// platform specific error_code. +error_code permissions(const Twine &path, perms prms); + /// @brief Is status available? /// /// @param path Input path. @@ -513,6 +576,33 @@ error_code FindLibrary(const Twine &short_name, SmallVectorImpl<char> &result); error_code GetMainExecutable(const char *argv0, void *MainAddr, SmallVectorImpl<char> &result); + +/// @brief Memory maps the contents of a file +/// +/// @param path Path to file to map. +/// @param file_offset Byte offset in file where mapping should begin. +/// @param size_t Byte length of range of the file to map. +/// @param map_writable If true, the file will be mapped in r/w such +/// that changes to the the mapped buffer will be flushed back +/// to the file. If false, the file will be mapped read-only +/// and the buffer will be read-only. +/// @param result Set to the start address of the mapped buffer. +/// @results errc::success if result has been successfully set, otherwise a +/// platform specific error_code. +error_code map_file_pages(const Twine &path, off_t file_offset, size_t size, + bool map_writable, void *&result); + + +/// @brief Memory unmaps the contents of a file +/// +/// @param base Pointer to the start of the buffer. +/// @param size Byte length of the range to unmmap. +/// @results errc::success if result has been successfully set, otherwise a +/// platform specific error_code. +error_code unmap_file_pages(void *base, size_t size); + + + /// @} /// @name Iterators /// @{ diff --git a/lib/Support/Unix/PathV2.inc b/lib/Support/Unix/PathV2.inc index a5630b9..4e431ca 100644 --- a/lib/Support/Unix/PathV2.inc +++ b/lib/Support/Unix/PathV2.inc @@ -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) @@ -325,20 +328,22 @@ error_code status(const Twine &path, file_status &result) { return ec; } + perms prms = static_cast<perms>(status.st_mode & perms_mask); + if (S_ISDIR(status.st_mode)) - result = file_status(file_type::directory_file); + result = file_status(file_type::directory_file, prms); else if (S_ISREG(status.st_mode)) - result = file_status(file_type::regular_file); + result = file_status(file_type::regular_file, prms); else if (S_ISBLK(status.st_mode)) - result = file_status(file_type::block_file); + result = file_status(file_type::block_file, prms); else if (S_ISCHR(status.st_mode)) - result = file_status(file_type::character_file); + result = file_status(file_type::character_file, prms); else if (S_ISFIFO(status.st_mode)) - result = file_status(file_type::fifo_file); + result = file_status(file_type::fifo_file, prms); else if (S_ISSOCK(status.st_mode)) - result = file_status(file_type::socket_file); + result = file_status(file_type::socket_file, prms); else - result = file_status(file_type::type_unknown); + result = file_status(file_type::type_unknown, prms); result.fs_st_dev = status.st_dev; result.fs_st_ino = status.st_ino; @@ -346,6 +351,35 @@ error_code status(const Twine &path, file_status &result) { return error_code::success(); } +// Modifies permissions on a file. +error_code permissions(const Twine &path, perms prms) { + if ((prms & add_perms) && (prms & remove_perms)) + llvm_unreachable("add_perms and remove_perms are mutually exclusive"); + + // Get current permissions + file_status info; + if (error_code ec = status(path, info)) { + return ec; + } + + // Set updated permissions. + SmallString<128> path_storage; + StringRef p = path.toNullTerminatedStringRef(path_storage); + perms permsToSet; + if (prms & add_perms) { + permsToSet = (info.permissions() | prms) & perms_mask; + } else if (prms & remove_perms) { + permsToSet = (info.permissions() & ~prms) & perms_mask; + } else { + permsToSet = prms & perms_mask; + } + if (::chmod(p.begin(), static_cast<mode_t>(permsToSet))) { + return error_code(errno, system_category()); + } + + return error_code::success(); +} + // Since this is most often used for temporary files, mode defaults to 0600. error_code unique_file(const Twine &model, int &result_fd, SmallVectorImpl<char> &result_path, @@ -495,6 +529,36 @@ error_code get_magic(const Twine &path, uint32_t len, 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 ofd = ::open(name.begin(), oflags); + if ( ofd == -1 ) + return error_code(errno, system_category()); + AutoFD fd(ofd); + 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) { + return error_code(errno, system_category()); + } + + 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(); +} + + } // end namespace fs } // end namespace sys } // end namespace llvm diff --git a/lib/Support/Windows/PathV2.inc b/lib/Support/Windows/PathV2.inc index 4868b18..c79d8ab 100644 --- a/lib/Support/Windows/PathV2.inc +++ b/lib/Support/Windows/PathV2.inc @@ -497,6 +497,41 @@ handle_status_error: return error_code::success(); } + +// Modifies permissions on a file. +error_code permissions(const Twine &path, perms prms) { +#if 0 // verify code below before enabling: + // If the permissions bits are not trying to modify + // "write" permissions, there is nothing to do. + if (!(prms & (owner_write|group_write|others_write))) + return error_code::success(); + + SmallString<128> path_storage; + SmallVector<wchar_t, 128> path_utf16; + + if (error_code ec = UTF8ToUTF16(path.toStringRef(path_storage), + path_utf16)) + return ec; + + DWORD attributes = ::GetFileAttributesW(path_utf16.begin()); + + if (prms & add_perms) { + attributes &= ~FILE_ATTRIBUTE_READONLY; + } + else if (prms & remove_perms) { + attributes |= FILE_ATTRIBUTE_READONLY; + } + else { + assert(0 && "neither add_perms or remove_perms is set"); + } + + if ( ! ::SetFileAttributesW(path_utf16.begin(), attributes)) + return windows_error(::GetLastError()); +#endif + return error_code::success(); +} + + // FIXME: mode should be used here and default to user r/w only, // it currently comes in as a UNIX mode. error_code unique_file(const Twine &model, int &result_fd, @@ -755,6 +790,17 @@ error_code detail::directory_iterator_increment(detail::DirIterState &it) { 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"); +} + + + } // end namespace fs } // end namespace sys } // end namespace llvm diff --git a/unittests/Support/Path.cpp b/unittests/Support/Path.cpp index 358dad0..766c990 100644 --- a/unittests/Support/Path.cpp +++ b/unittests/Support/Path.cpp @@ -312,4 +312,69 @@ TEST_F(FileSystemTest, Magic) { } } + +TEST_F(FileSystemTest, Permissions) { + // Create a temp file. + int FileDescriptor; + SmallString<64> TempPath; + ASSERT_NO_ERROR( + fs::unique_file("%%-%%-%%-%%.temp", FileDescriptor, TempPath)); + + // Mark file as read-only + const fs::perms AllWrite = fs::owner_write|fs::group_write|fs::others_write; + ASSERT_NO_ERROR(fs::permissions(Twine(TempPath), fs::remove_perms|AllWrite)); + + // Verify file is read-only + fs::file_status Status; + ASSERT_NO_ERROR(fs::status(Twine(TempPath), Status)); + bool AnyWriteBits = (Status.permissions() & AllWrite); + EXPECT_FALSE(AnyWriteBits); + + // Mark file as read-write + ASSERT_NO_ERROR(fs::permissions(Twine(TempPath), fs::add_perms|AllWrite)); + + // Verify file is read-write + ASSERT_NO_ERROR(fs::status(Twine(TempPath), Status)); + AnyWriteBits = (Status.permissions() & AllWrite); + EXPECT_TRUE(AnyWriteBits); +} + +TEST_F(FileSystemTest, FileMapping) { + // Create a temp file. + int FileDescriptor; + SmallString<64> TempPath; + ASSERT_NO_ERROR( + fs::unique_file("%%-%%-%%-%%.temp", FileDescriptor, TempPath)); + + // Grow temp file to be 4096 bytes + ASSERT_NO_ERROR(sys::fs::resize_file(Twine(TempPath), 4096)); + + // Map in temp file and add some content + void* MappedMemory; + ASSERT_NO_ERROR(fs::map_file_pages(Twine(TempPath), 0, 4096, + true /*writable*/, MappedMemory)); + char* Memory = reinterpret_cast<char*>(MappedMemory); + strcpy(Memory, "hello there"); + + // Unmap temp file + ASSERT_NO_ERROR(fs::unmap_file_pages(MappedMemory, 4096)); + MappedMemory = NULL; + Memory = NULL; + + // Map it back in read-only + ASSERT_NO_ERROR(fs::map_file_pages(Twine(TempPath), 0, 4096, + false /*read-only*/, MappedMemory)); + + // Verify content + Memory = reinterpret_cast<char*>(MappedMemory); + bool SAME = (strcmp(Memory, "hello there") == 0); + EXPECT_TRUE(SAME); + + // Unmap temp file + ASSERT_NO_ERROR(fs::unmap_file_pages(MappedMemory, 4096)); + MappedMemory = NULL; + Memory = NULL; +} + + } // anonymous namespace |