diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6e661ca7..b2f993fb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -515,6 +515,12 @@ list(APPEND LIBDWARFS_SRC
src/dwarfs/xattr.cpp
)
+if(WIN32)
+ list(APPEND LIBDWARFS_SRC src/dwarfs/xattr_win.cpp)
+else()
+ list(APPEND LIBDWARFS_SRC src/dwarfs/xattr_posix.cpp)
+endif()
+
if(WITH_MAN_OPTION)
include(${CMAKE_SOURCE_DIR}/cmake/render_manpage.cmake)
diff --git a/src/dwarfs/xattr.cpp b/src/dwarfs/xattr.cpp
index 18f6d7c0..f9709449 100644
--- a/src/dwarfs/xattr.cpp
+++ b/src/dwarfs/xattr.cpp
@@ -19,369 +19,10 @@
* along with dwarfs. If not, see .
*/
-#ifdef _WIN32
-
-#include
-
-#include
-#include
-#include
-#include
-
-#include
-
-#else
-
-#include
-
-#endif
-
-#include
-#include
-
-#include
-
#include "dwarfs/xattr.h"
-#ifdef _WIN32
-
-extern "C" {
-
-typedef struct _FILE_FULL_EA_INFORMATION {
- ULONG NextEntryOffset;
- UCHAR Flags;
- UCHAR EaNameLength;
- USHORT EaValueLength;
- CHAR EaName[1];
-} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;
-
-typedef struct _FILE_GET_EA_INFORMATION {
- ULONG NextEntryOffset;
- UCHAR EaNameLength;
- CHAR EaName[1];
-} FILE_GET_EA_INFORMATION, *PFILE_GET_EA_INFORMATION;
-
-NTSYSAPI NTSTATUS NTAPI RtlDosPathNameToNtPathName_U_WithStatus(
- PCWSTR DosFileName, PUNICODE_STRING NtFileName, PWSTR* FilePart,
- PVOID RelativeName);
-
-VOID NTAPI RtlFreeUnicodeString(PUNICODE_STRING UnicodeString);
-
-NTSYSAPI NTSTATUS NTAPI NtQueryEaFile(HANDLE FileHandle,
- PIO_STATUS_BLOCK IoStatusBlock,
- PVOID Buffer, ULONG Length,
- BOOLEAN ReturnSingleEntry, PVOID EaList,
- ULONG EaListLength, PULONG EaIndex,
- BOOLEAN RestartScan);
-
-NTSYSAPI NTSTATUS NTAPI NtSetEaFile(HANDLE FileHandle,
- PIO_STATUS_BLOCK IoStatusBlock,
- PVOID Buffer, ULONG Length);
-}
-
-#endif
-
namespace dwarfs {
-namespace {
-
-#ifdef _WIN32
-
-constexpr size_t kMaxFullEaBufferSize{
- offsetof(FILE_FULL_EA_INFORMATION, EaName) + 256 + 65536};
-constexpr size_t kMaxGetEaBufferSize{offsetof(FILE_GET_EA_INFORMATION, EaName) +
- 256};
-
-HANDLE open_file(std::filesystem::path const& path, bool writeable,
- std::error_code& ec) {
- UNICODE_STRING nt_path;
-
- if (auto r = ::RtlDosPathNameToNtPathName_U_WithStatus(
- path.wstring().c_str(), &nt_path, nullptr, nullptr);
- r != 0) {
- ec = std::error_code(r, std::system_category());
- return nullptr;
- }
-
- SCOPE_EXIT { ::RtlFreeUnicodeString(&nt_path); };
-
- HANDLE fh;
- IO_STATUS_BLOCK iosb;
- OBJECT_ATTRIBUTES attr;
- ACCESS_MASK desired_access = FILE_READ_EA;
-
- if (writeable) {
- desired_access |= FILE_WRITE_EA;
- }
-
- InitializeObjectAttributes(&attr, &nt_path, 0, nullptr, nullptr);
-
- if (auto r = ::NtCreateFile(
- &fh, desired_access, &attr, &iosb, nullptr, FILE_ATTRIBUTE_NORMAL,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, 0,
- nullptr, 0);
- r != 0) {
- ec = std::error_code(::RtlNtStatusToDosError(r), std::system_category());
- return nullptr;
- }
-
- return fh;
-}
-
-#else
-
-ssize_t portable_getxattr(const char* path, const char* name, void* value,
- size_t size) {
-#ifdef __APPLE__
- return ::getxattr(path, name, value, size, 0, 0);
-#else
- return ::getxattr(path, name, value, size);
-#endif
-}
-
-int portable_setxattr(const char* path, const char* name, const void* value,
- size_t size, int flags) {
-#ifdef __APPLE__
- return ::setxattr(path, name, value, size, 0, flags);
-#else
- return ::setxattr(path, name, value, size, flags);
-#endif
-}
-
-int portable_removexattr(const char* path, const char* name) {
-#ifdef __APPLE__
- return ::removexattr(path, name, 0);
-#else
- return ::removexattr(path, name);
-#endif
-}
-
-ssize_t portable_listxattr(const char* path, char* list, size_t size) {
-#ifdef __APPLE__
- return ::listxattr(path, list, size, 0);
-#else
- return ::listxattr(path, list, size);
-#endif
-}
-
-#endif
-
-constexpr size_t kExtraSize{1024};
-
-} // namespace
-
-#ifdef _WIN32
-
-std::string getxattr(std::filesystem::path const& path, std::string const& name,
- std::error_code& ec) {
- ec.clear();
-
- if (name.size() > std::numeric_limits::max()) {
- ec = std::error_code(ERROR_INVALID_EA_NAME, std::system_category());
- return {};
- }
-
- auto fh = open_file(path, false, ec);
-
- if (!fh) {
- // error code already set
- return {};
- }
-
- SCOPE_EXIT { ::NtClose(fh); };
-
- CHAR getea_buf[kMaxGetEaBufferSize];
- ULONG getea_len =
- FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + name.size() + 1;
- auto getea = reinterpret_cast(getea_buf);
-
- getea->NextEntryOffset = 0;
- getea->EaNameLength = static_cast(name.size());
- std::memcpy(getea->EaName, name.data(), name.size());
- getea->EaName[name.size()] = '\0';
-
- std::vector ea_buf(kMaxFullEaBufferSize);
- PFILE_FULL_EA_INFORMATION ea;
- IO_STATUS_BLOCK iosb;
-
- ea = reinterpret_cast(ea_buf.data());
-
- auto res = ::NtQueryEaFile(fh, &iosb, ea, ea_buf.size(), FALSE, getea,
- getea_len, nullptr, FALSE);
-
- if (res != STATUS_SUCCESS) {
- ec = std::error_code(::RtlNtStatusToDosError(res), std::system_category());
- return {};
- }
-
- if (ea->EaValueLength == 0) {
- ec = std::error_code(ENODATA, std::generic_category());
- return {};
- }
-
- return {ea->EaName + ea->EaNameLength + 1, ea->EaValueLength};
-}
-
-void setxattr(std::filesystem::path const& path, std::string const& name,
- std::string_view value, std::error_code& ec) {
- // TODO
-}
-
-void removexattr(std::filesystem::path const& path, std::string const& name,
- std::error_code& ec) {
- // TODO
-}
-
-std::vector
-listxattr(std::filesystem::path const& path, std::error_code& ec) {
- ec.clear();
-
- auto fh = open_file(path, false, ec);
-
- if (!fh) {
- // error code already set
- return {};
- }
-
- SCOPE_EXIT { ::NtClose(fh); };
-
- std::vector names;
- std::vector ea_buf(kMaxFullEaBufferSize);
- BOOLEAN restart = TRUE;
-
- for (;;) {
- IO_STATUS_BLOCK iosb;
-
- auto ea = reinterpret_cast(ea_buf.data());
- auto res = ::NtQueryEaFile(fh, &iosb, ea, ea_buf.size(), FALSE, nullptr, 0,
- nullptr, restart);
-
- if (res != STATUS_SUCCESS && res != STATUS_BUFFER_OVERFLOW) {
- ec =
- std::error_code(::RtlNtStatusToDosError(res), std::system_category());
- return {};
- }
-
- for (;;) {
- std::string name(ea->EaName, ea->EaNameLength);
- boost::algorithm::to_lower(name);
- names.push_back(std::move(name));
-
- if (ea->NextEntryOffset == 0) {
- break;
- }
-
- ea = reinterpret_cast(
- reinterpret_cast(ea) + ea->NextEntryOffset);
- }
-
- if (res == STATUS_SUCCESS) {
- break;
- }
-
- restart = FALSE;
- }
-
- return names;
-}
-
-#else
-
-std::string getxattr(std::filesystem::path const& path, std::string const& name,
- std::error_code& ec) {
- ec.clear();
-
- auto cpath = path.c_str();
- auto cname = name.c_str();
-
- for (;;) {
- ssize_t size = portable_getxattr(cpath, cname, nullptr, 0);
-
- if (size < 0) {
- break;
- }
-
- std::string value;
- value.resize(size + kExtraSize);
-
- size = portable_getxattr(cpath, cname, value.data(), value.size());
-
- if (size >= 0) {
- value.resize(size);
- return value;
- }
-
- if (errno != ERANGE) {
- break;
- }
- }
-
- ec = std::error_code(errno, std::generic_category());
-
- return {};
-}
-
-void setxattr(std::filesystem::path const& path, std::string const& name,
- std::string_view value, std::error_code& ec) {
- ec.clear();
-
- if (portable_setxattr(path.c_str(), name.c_str(), value.data(), value.size(),
- 0) < 0) {
- ec = std::error_code(errno, std::generic_category());
- }
-}
-
-void removexattr(std::filesystem::path const& path, std::string const& name,
- std::error_code& ec) {
- ec.clear();
-
- if (portable_removexattr(path.c_str(), name.c_str()) < 0) {
- ec = std::error_code(errno, std::generic_category());
- }
-}
-
-std::vector
-listxattr(std::filesystem::path const& path, std::error_code& ec) {
- ec.clear();
-
- auto cpath = path.c_str();
-
- for (;;) {
- ssize_t size = portable_listxattr(cpath, nullptr, 0);
-
- if (size < 0) {
- break;
- }
-
- std::string list;
- list.resize(size + kExtraSize);
-
- size = portable_listxattr(cpath, list.data(), list.size());
-
- if (size >= 0) {
- std::vector names;
-
- if (size > 0) {
- // drop the last '\0'
- list.resize(size - 1);
- folly::split('\0', list, names);
- }
-
- return names;
- }
-
- if (errno != ERANGE) {
- break;
- }
- }
-
- ec = std::error_code(errno, std::generic_category());
-
- return {};
-}
-
-#endif
-
std::string
getxattr(std::filesystem::path const& path, std::string const& name) {
std::error_code ec;
diff --git a/src/dwarfs/xattr_posix.cpp b/src/dwarfs/xattr_posix.cpp
new file mode 100644
index 00000000..296fe7e3
--- /dev/null
+++ b/src/dwarfs/xattr_posix.cpp
@@ -0,0 +1,163 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/**
+ * \author Marcus Holland-Moritz (github@mhxnet.de)
+ * \copyright Copyright (c) Marcus Holland-Moritz
+ *
+ * This file is part of dwarfs.
+ *
+ * dwarfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dwarfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dwarfs. If not, see .
+ */
+
+#include
+
+#include
+
+#include "dwarfs/xattr.h"
+
+namespace dwarfs {
+
+namespace {
+
+ssize_t portable_getxattr(const char* path, const char* name, void* value,
+ size_t size) {
+#ifdef __APPLE__
+ return ::getxattr(path, name, value, size, 0, 0);
+#else
+ return ::getxattr(path, name, value, size);
+#endif
+}
+
+int portable_setxattr(const char* path, const char* name, const void* value,
+ size_t size, int flags) {
+#ifdef __APPLE__
+ return ::setxattr(path, name, value, size, 0, flags);
+#else
+ return ::setxattr(path, name, value, size, flags);
+#endif
+}
+
+int portable_removexattr(const char* path, const char* name) {
+#ifdef __APPLE__
+ return ::removexattr(path, name, 0);
+#else
+ return ::removexattr(path, name);
+#endif
+}
+
+ssize_t portable_listxattr(const char* path, char* list, size_t size) {
+#ifdef __APPLE__
+ return ::listxattr(path, list, size, 0);
+#else
+ return ::listxattr(path, list, size);
+#endif
+}
+
+constexpr size_t kExtraSize{1024};
+
+} // namespace
+
+std::string getxattr(std::filesystem::path const& path, std::string const& name,
+ std::error_code& ec) {
+ ec.clear();
+
+ auto cpath = path.c_str();
+ auto cname = name.c_str();
+
+ for (;;) {
+ ssize_t size = portable_getxattr(cpath, cname, nullptr, 0);
+
+ if (size < 0) {
+ break;
+ }
+
+ std::string value;
+ value.resize(size + kExtraSize);
+
+ size = portable_getxattr(cpath, cname, value.data(), value.size());
+
+ if (size >= 0) {
+ value.resize(size);
+ return value;
+ }
+
+ if (errno != ERANGE) {
+ break;
+ }
+ }
+
+ ec = std::error_code(errno, std::generic_category());
+
+ return {};
+}
+
+void setxattr(std::filesystem::path const& path, std::string const& name,
+ std::string_view value, std::error_code& ec) {
+ ec.clear();
+
+ if (portable_setxattr(path.c_str(), name.c_str(), value.data(), value.size(),
+ 0) < 0) {
+ ec = std::error_code(errno, std::generic_category());
+ }
+}
+
+void removexattr(std::filesystem::path const& path, std::string const& name,
+ std::error_code& ec) {
+ ec.clear();
+
+ if (portable_removexattr(path.c_str(), name.c_str()) < 0) {
+ ec = std::error_code(errno, std::generic_category());
+ }
+}
+
+std::vector
+listxattr(std::filesystem::path const& path, std::error_code& ec) {
+ ec.clear();
+
+ auto cpath = path.c_str();
+
+ for (;;) {
+ ssize_t size = portable_listxattr(cpath, nullptr, 0);
+
+ if (size < 0) {
+ break;
+ }
+
+ std::string list;
+ list.resize(size + kExtraSize);
+
+ size = portable_listxattr(cpath, list.data(), list.size());
+
+ if (size >= 0) {
+ std::vector names;
+
+ if (size > 0) {
+ // drop the last '\0'
+ list.resize(size - 1);
+ folly::split('\0', list, names);
+ }
+
+ return names;
+ }
+
+ if (errno != ERANGE) {
+ break;
+ }
+ }
+
+ ec = std::error_code(errno, std::generic_category());
+
+ return {};
+}
+
+} // namespace dwarfs
diff --git a/src/dwarfs/xattr_win.cpp b/src/dwarfs/xattr_win.cpp
new file mode 100644
index 00000000..2f2926dc
--- /dev/null
+++ b/src/dwarfs/xattr_win.cpp
@@ -0,0 +1,229 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/**
+ * \author Marcus Holland-Moritz (github@mhxnet.de)
+ * \copyright Copyright (c) Marcus Holland-Moritz
+ *
+ * This file is part of dwarfs.
+ *
+ * dwarfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * dwarfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with dwarfs. If not, see .
+ */
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+
+#include "dwarfs/xattr.h"
+
+extern "C" {
+
+typedef struct _FILE_FULL_EA_INFORMATION {
+ ULONG NextEntryOffset;
+ UCHAR Flags;
+ UCHAR EaNameLength;
+ USHORT EaValueLength;
+ CHAR EaName[1];
+} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;
+
+typedef struct _FILE_GET_EA_INFORMATION {
+ ULONG NextEntryOffset;
+ UCHAR EaNameLength;
+ CHAR EaName[1];
+} FILE_GET_EA_INFORMATION, *PFILE_GET_EA_INFORMATION;
+
+NTSYSAPI NTSTATUS NTAPI RtlDosPathNameToNtPathName_U_WithStatus(
+ PCWSTR DosFileName, PUNICODE_STRING NtFileName, PWSTR* FilePart,
+ PVOID RelativeName);
+
+VOID NTAPI RtlFreeUnicodeString(PUNICODE_STRING UnicodeString);
+
+NTSYSAPI NTSTATUS NTAPI NtQueryEaFile(HANDLE FileHandle,
+ PIO_STATUS_BLOCK IoStatusBlock,
+ PVOID Buffer, ULONG Length,
+ BOOLEAN ReturnSingleEntry, PVOID EaList,
+ ULONG EaListLength, PULONG EaIndex,
+ BOOLEAN RestartScan);
+
+NTSYSAPI NTSTATUS NTAPI NtSetEaFile(HANDLE FileHandle,
+ PIO_STATUS_BLOCK IoStatusBlock,
+ PVOID Buffer, ULONG Length);
+}
+
+namespace dwarfs {
+
+namespace {
+
+constexpr size_t kMaxFullEaBufferSize{
+ offsetof(FILE_FULL_EA_INFORMATION, EaName) + 256 + 65536};
+constexpr size_t kMaxGetEaBufferSize{offsetof(FILE_GET_EA_INFORMATION, EaName) +
+ 256};
+
+HANDLE open_file(std::filesystem::path const& path, bool writeable,
+ std::error_code& ec) {
+ UNICODE_STRING nt_path;
+
+ if (auto r = ::RtlDosPathNameToNtPathName_U_WithStatus(
+ path.wstring().c_str(), &nt_path, nullptr, nullptr);
+ r != 0) {
+ ec = std::error_code(r, std::system_category());
+ return nullptr;
+ }
+
+ SCOPE_EXIT { ::RtlFreeUnicodeString(&nt_path); };
+
+ HANDLE fh;
+ IO_STATUS_BLOCK iosb;
+ OBJECT_ATTRIBUTES attr;
+ ACCESS_MASK desired_access = FILE_READ_EA;
+
+ if (writeable) {
+ desired_access |= FILE_WRITE_EA;
+ }
+
+ InitializeObjectAttributes(&attr, &nt_path, 0, nullptr, nullptr);
+
+ if (auto r = ::NtCreateFile(
+ &fh, desired_access, &attr, &iosb, nullptr, FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, 0,
+ nullptr, 0);
+ r != 0) {
+ ec = std::error_code(::RtlNtStatusToDosError(r), std::system_category());
+ return nullptr;
+ }
+
+ return fh;
+}
+
+} // namespace
+
+std::string getxattr(std::filesystem::path const& path, std::string const& name,
+ std::error_code& ec) {
+ ec.clear();
+
+ if (name.size() > std::numeric_limits::max()) {
+ ec = std::error_code(ERROR_INVALID_EA_NAME, std::system_category());
+ return {};
+ }
+
+ auto fh = open_file(path, false, ec);
+
+ if (!fh) {
+ // error code already set
+ return {};
+ }
+
+ SCOPE_EXIT { ::NtClose(fh); };
+
+ CHAR getea_buf[kMaxGetEaBufferSize];
+ ULONG getea_len =
+ FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) + name.size() + 1;
+ auto getea = reinterpret_cast(getea_buf);
+
+ getea->NextEntryOffset = 0;
+ getea->EaNameLength = static_cast(name.size());
+ std::memcpy(getea->EaName, name.data(), name.size());
+ getea->EaName[name.size()] = '\0';
+
+ std::vector ea_buf(kMaxFullEaBufferSize);
+ PFILE_FULL_EA_INFORMATION ea;
+ IO_STATUS_BLOCK iosb;
+
+ ea = reinterpret_cast(ea_buf.data());
+
+ auto res = ::NtQueryEaFile(fh, &iosb, ea, ea_buf.size(), FALSE, getea,
+ getea_len, nullptr, FALSE);
+
+ if (res != STATUS_SUCCESS) {
+ ec = std::error_code(::RtlNtStatusToDosError(res), std::system_category());
+ return {};
+ }
+
+ if (ea->EaValueLength == 0) {
+ ec = std::error_code(ENODATA, std::generic_category());
+ return {};
+ }
+
+ return {ea->EaName + ea->EaNameLength + 1, ea->EaValueLength};
+}
+
+void setxattr(std::filesystem::path const& path, std::string const& name,
+ std::string_view value, std::error_code& ec) {
+ // TODO
+}
+
+void removexattr(std::filesystem::path const& path, std::string const& name,
+ std::error_code& ec) {
+ // TODO
+}
+
+std::vector
+listxattr(std::filesystem::path const& path, std::error_code& ec) {
+ ec.clear();
+
+ auto fh = open_file(path, false, ec);
+
+ if (!fh) {
+ // error code already set
+ return {};
+ }
+
+ SCOPE_EXIT { ::NtClose(fh); };
+
+ std::vector names;
+ std::vector ea_buf(kMaxFullEaBufferSize);
+ BOOLEAN restart = TRUE;
+
+ for (;;) {
+ IO_STATUS_BLOCK iosb;
+
+ auto ea = reinterpret_cast(ea_buf.data());
+ auto res = ::NtQueryEaFile(fh, &iosb, ea, ea_buf.size(), FALSE, nullptr, 0,
+ nullptr, restart);
+
+ if (res != STATUS_SUCCESS && res != STATUS_BUFFER_OVERFLOW) {
+ ec =
+ std::error_code(::RtlNtStatusToDosError(res), std::system_category());
+ return {};
+ }
+
+ for (;;) {
+ std::string name(ea->EaName, ea->EaNameLength);
+ boost::algorithm::to_lower(name);
+ names.push_back(std::move(name));
+
+ if (ea->NextEntryOffset == 0) {
+ break;
+ }
+
+ ea = reinterpret_cast(
+ reinterpret_cast(ea) + ea->NextEntryOffset);
+ }
+
+ if (res == STATUS_SUCCESS) {
+ break;
+ }
+
+ restart = FALSE;
+ }
+
+ return names;
+}
+
+} // namespace dwarfs