Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support password delegates #16

Merged
merged 1 commit into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required (VERSION 3.12 FATAL_ERROR)

project (fuse3-p7zip
VERSION 2.0.0
VERSION 2.1.0
DESCRIPTION "fuse3 file system that uses the 7zip library to mount archives"
LANGUAGES CXX
)
Expand Down
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ Change where application searches for 7z.so library. By default this is `/usr/li

Provide a password which will be used if archive is protected with a password.

* FUSE3_P7ZIP_PASSFILE

Provide a password file which will be used if archive is protected with a password.

If executable file is provided then its output to /dev/stdout will be used as a password.
If non-executable file is provided then its content will be used as a password.

* FUSE3_P7ZIP_FORCE_ENCODING

Some archives (especially those ones created on windows) have file names encoded in non Unicode encoding. This
Expand Down Expand Up @@ -97,6 +104,23 @@ To check the list of all available formats run `7z i` command

## Examples

### password protected archive, request password interactively (KDE password popup window)

```bash
$ fuse3-p7zip example/archive.7z /tmp/mnt --passfile=example/password-kde
```

```bash
$ export FUSE3_P7ZIP_PASSFILE=example/password-kde
$ fuse3-p7zip example/archive.7z /tmp/mnt
```

### password protected archive, fetch password from a password storage

```bash
$ fuse3-p7zip example/archive.7z /tmp/mnt --passfile=example/password-pass
```

### vifm config snippet (mount 7zip supported archive with fuse3-p7zip in vifm)

> I suggest to use [archivemount](https://github.com/cybernoid/archivemount) for all tar-based archives as a priority and
Expand Down
Binary file added example/archive.7z
Binary file not shown.
1 change: 1 addition & 0 deletions example/pass
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SECRET
3 changes: 3 additions & 0 deletions example/password-kde
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash

kdialog --password "Enter password for: $1"
5 changes: 5 additions & 0 deletions example/password-pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash

echo "Password for $1" >/dev/stderr

pass show 'password/from/pass'
2 changes: 2 additions & 0 deletions include/string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <string>
#include <vector>

std::string chomp(const std::string& str);

std::string utf8raw(const std::wstring& wstr, const char* encoding);

std::string utf8(const wchar_t* str);
Expand Down
108 changes: 92 additions & 16 deletions src/fuse3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "string.hpp"
#include "version.hpp"

#include <array>
#include <cstring>
#include <fstream>
#include <filesystem>
Expand Down Expand Up @@ -37,7 +38,71 @@ static struct cmd_params_t {
static const fuse_opt opts_spec[] = {CMD_OPT("--password=%s", password), CMD_OPT("-p %s", password),
CMD_OPT("--passfile=%s", passfile), FUSE_OPT_END};

static std::string cmd_password;
class IPassword {
public:
virtual ~IPassword() noexcept = default;

virtual std::string get() noexcept = 0;
};

class PasswordFromString: public IPassword {
public:
PasswordFromString(const std::string& password) noexcept
: _password(password)
{}
std::string get() noexcept
{
return _password;
}

private:
std::string _password;
};

class PasswordFromFile: public IPassword {
public:
PasswordFromFile(const std::filesystem::path& path, const std::filesystem::path& arc) noexcept
: _archive(arc)
, _path(path)
{}
std::string get() noexcept
{
if (_password.empty()) {
if (access(_path.c_str(), X_OK) == 0) {
_password = chomp(exec(_path));
} else {
std::ifstream passfile(_path);
std::istreambuf_iterator it(passfile);
std::string pass(it, {});
_password = chomp(pass);
}
}
return _password;
}

private:
std::string exec(const std::filesystem::path& path)
{
std::array<char, 1024> buffer;
std::string result;
auto cmd = std::string(path.c_str()) + " '" + _archive.c_str() + "'";
std::unique_ptr<FILE, int (*)(FILE*)> pipe(popen(cmd.c_str(), "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe.get()) != nullptr) {
result += buffer.data();
}

return result;
}

std::filesystem::path _archive;
std::filesystem::path _path;
std::string _password;
};

static std::unique_ptr<IPassword> cmd_password = std::make_unique<PasswordFromString>(std::string());

class Fuse::Params: public fuse_cmdline_opts {
public:
Expand Down Expand Up @@ -65,20 +130,6 @@ Fuse::Params::Params(int argc, char** argv)

CheckResult(fuse_parse_cmdline(&args, this) == 0, "fuse_parse_cmdline");

if (cmd_params.password) {
cmd_password = cmd_params.password;
} else if (cmd_params.passfile) {
std::ifstream passfile(cmd_params.passfile);
std::istreambuf_iterator it(passfile);
std::string pass(it, {});
cmd_password = pass;
} else {
auto password = std::getenv("FUSE3_P7ZIP_PASSWORD");
if (password) {
cmd_password = password;
}
}

if (show_version) {
printf("%s\n", PROJECT_VERSION);
// printf("FUSE library version %s\n", fuse_pkgversion());
Expand All @@ -93,6 +144,31 @@ Fuse::Params::Params(int argc, char** argv)
fprintf(stderr, "error: no mountpoint specified\n");
exit(0);
}

auto env_password = std::getenv("FUSE3_P7ZIP_PASSWORD");
if (env_password) {
LogDebug("using FUSE3_P7ZIP_PASSWORD\n");
cmd_password.reset(new PasswordFromString(env_password));
}

if (cmd_params.password) {
LogDebug("using --password\n");
cmd_password.reset(new PasswordFromString(cmd_params.password));
}

auto env_passfile = std::getenv("FUSE3_P7ZIP_PASSFILE");
if (env_passfile && std::filesystem::exists(env_passfile) && std::filesystem::is_regular_file(env_passfile)) {
auto path = std::filesystem::absolute(std::filesystem::path(env_passfile));
LogDebug("using FUSE3_P7ZIP_PASSFILE: %s\n", path.c_str());
cmd_password.reset(new PasswordFromFile(path, cmd_params.cli_args[0]));
}

if (cmd_params.passfile && std::filesystem::exists(cmd_params.passfile) &&
std::filesystem::is_regular_file(cmd_params.passfile)) {
auto path = std::filesystem::absolute(std::filesystem::path(cmd_params.passfile));
LogDebug("using --passfile: %s\n", path.c_str());
cmd_password.reset(new PasswordFromFile(path, cmd_params.cli_args[0]));
}
}

void Fuse::Params::print_usage()
Expand Down Expand Up @@ -249,7 +325,7 @@ const std::string& Fuse::path() const

std::string Fuse::password() const
{
return cmd_password;
return cmd_password->get();
}

ssize_t Fuse::execute(sevenzip::IArchive* arc)
Expand Down
9 changes: 9 additions & 0 deletions src/string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
#include <memory>
#include <iconv.h>

std::string chomp(const std::string& str)
{
auto pos = str.find_last_not_of("\n");
if (pos != std::string::npos) {
return str.substr(0, pos + 1);
}
return str;
}

std::string utf8raw(const std::wstring& wstr, const char* encoding)
{
return utf8(std::string(wstr.begin(), wstr.end()).c_str(), encoding);
Expand Down