diff --git a/CMakeLists.txt b/CMakeLists.txt
index 63eb6f07..3f9a6f73 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -35,6 +35,7 @@ option(ENABLE_PERFMON "enable performance monitor in all tools" ON)
option(ENABLE_FLAC "build with FLAC support" ON)
option(ENABLE_RICEPP "build with RICEPP compression support" ON)
option(WITH_UNIVERSAL_BINARY "build with universal binary" ON)
+option(WITH_PXATTR "build with pxattr binary" OFF)
if(APPLE)
option(USE_HOMEBREW_LIBARCHIVE "use libarchive from homebrew" ON)
endif()
@@ -643,6 +644,11 @@ if(WITH_UNIVERSAL_BINARY)
OUTPUT_NAME dwarfs-universal)
endif()
+if(WITH_PXATTR)
+ add_executable(pxattr src/pxattr.cpp)
+ list(APPEND BINARY_TARGETS pxattr)
+endif()
+
install(TARGETS mkdwarfs dwarfsck dwarfsbench dwarfsextract RUNTIME DESTINATION bin)
list(APPEND MAIN_TARGETS mkdwarfs_main dwarfsck_main dwarfsbench_main
diff --git a/src/pxattr.cpp b/src/pxattr.cpp
new file mode 100644
index 00000000..4c44e910
--- /dev/null
+++ b/src/pxattr.cpp
@@ -0,0 +1,131 @@
+/* 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/program_options_helpers.h"
+#include "dwarfs/types.h"
+#include "dwarfs/xattr.h"
+
+namespace dwarfs {
+
+namespace po = boost::program_options;
+namespace fs = std::filesystem;
+
+int pxattr_main(int argc, sys_char** argv) {
+ std::string name, value;
+ sys_string pathstr;
+
+ // clang-format off
+ po::options_description desc("Command line options");
+ desc.add_options()
+ ("get,g", po::value(&name), "get extended attribute value")
+ ("set,s", po::value(&name), "set extended attribute value")
+ ("remove,r", po::value(&name), "remove extended attribute")
+ ("list,l", "list extended attributes")
+ ("path", po_sys_value(&pathstr), "path to the file or directory")
+ ("value,V", po::value(&value), "new attribute value (with -s)")
+ ("help,h", "show this help message")
+ ;
+ // clang-format on
+
+ po::positional_options_description pos;
+ pos.add("path", 1);
+
+ po::variables_map vm;
+ po::store(po::basic_command_line_parser(argc, argv)
+ .options(desc)
+ .positional(pos)
+ .run(),
+ vm);
+ po::notify(vm);
+
+ if (vm.count("help")) {
+ std::cout << desc << std::endl;
+ return 0;
+ }
+
+ if (!vm.count("path")) {
+ std::cerr << "no path specified" << std::endl;
+ return 1;
+ }
+
+ if (vm.count("get") + vm.count("set") + vm.count("remove") +
+ vm.count("list") !=
+ 1) {
+ std::cerr << "exactly one of --get, --set, --remove or --list must be "
+ "specified\n";
+ return 1;
+ }
+
+ if (vm.count("set") != vm.count("value")) {
+ std::cerr << "missing value for --set\n";
+ return 1;
+ }
+
+ fs::path path(pathstr);
+
+ if (vm.count("get")) {
+ std::error_code ec;
+ std::string val = getxattr(path, name, ec);
+ if (ec) {
+ std::cerr << "getxattr failed: " << ec.message() << "\n";
+ return 1;
+ }
+ std::cout << val << "\n";
+ } else if (vm.count("set")) {
+ std::error_code ec;
+ setxattr(path, name, value, ec);
+ if (ec) {
+ std::cerr << "setxattr failed: " << ec.message() << "\n";
+ return 1;
+ }
+ } else if (vm.count("remove")) {
+ std::error_code ec;
+ removexattr(path, name, ec);
+ if (ec) {
+ std::cerr << "removexattr failed: " << ec.message() << "\n";
+ return 1;
+ }
+ } else if (vm.count("list")) {
+ std::error_code ec;
+ std::vector attrs = listxattr(path, ec);
+ if (ec) {
+ std::cerr << "listxattr failed: " << ec.message() << "\n";
+ return 1;
+ }
+ for (const auto& attr : attrs) {
+ std::cout << attr << "\n";
+ }
+ }
+
+ std::cout << "successfully completed\n";
+
+ return 0;
+}
+
+} // namespace dwarfs
+
+int SYS_MAIN(int argc, dwarfs::sys_char** argv) {
+ return dwarfs::pxattr_main(argc, argv);
+}