From 67cd9ae964dafdf2bab87c92bee63cd20dc6ba05 Mon Sep 17 00:00:00 2001 From: Glenn Date: Tue, 24 Oct 2023 15:53:28 +0200 Subject: [PATCH] feat: add support for sshfs --- src/cli.cr | 2 +- src/config.cr | 23 +++++++++--------- src/filesystems.cr | 3 +++ src/filesystems/filesystem.cr | 38 ++++++++++++++++++++++++++++++ src/filesystems/gocryptfs.cr | 37 +++++++++++++++-------------- src/filesystems/sshfs.cr | 44 +++++++++++++++++++++++++++++++++++ 6 files changed, 117 insertions(+), 30 deletions(-) create mode 100644 src/filesystems.cr create mode 100644 src/filesystems/filesystem.cr create mode 100644 src/filesystems/sshfs.cr diff --git a/src/cli.cr b/src/cli.cr index cd5da39..bb6f941 100644 --- a/src/cli.cr +++ b/src/cli.cr @@ -67,7 +67,7 @@ module GX def run() @config.load_from_file - names_display = {} of String => NamedTuple(filesystem: GoCryptFS, ansi_name: String) + names_display = {} of String => NamedTuple(filesystem: Filesystem, ansi_name: String) @config.filesystems.each do |filesystem| result_name = filesystem.mounted? ? "#{filesystem.name} [open]" : filesystem.name ansi_name = filesystem.mounted? ? "#{filesystem.name} [#{ "open".colorize(:green) }]" : filesystem.name diff --git a/src/config.cr b/src/config.cr index 393f5c1..e18b5ab 100644 --- a/src/config.cr +++ b/src/config.cr @@ -1,5 +1,5 @@ -require "./filesystems/gocryptfs" +require "./filesystems" module GX class Config @@ -13,7 +13,7 @@ module GX record AddArgs, name : String, path : String record DelArgs, name : String - getter filesystems : Array(GoCryptFS) + getter filesystems : Array(Filesystem) getter home_dir : String property mode : Mode property path : String @@ -28,30 +28,31 @@ module GX @home_dir = ENV["HOME"] @mode = Mode::Run - @filesystems = [] of GoCryptFS + @filesystems = [] of Filesystem @path = File.join(@home_dir, ".config", DEFAULT_CONFIG_PATH) @args = NoArgs end def load_from_file - @filesystems = [] of GoCryptFS + @filesystems = [] of Filesystem if !File.exists? @path STDERR.puts "Error: file #{@path} does not exist!".colorize(:red) exit(1) end - load_vaults(@path) + load_filesystems(@path) end - private def load_vaults(config_path : String) + private def load_filesystems(config_path : String) yaml_data = YAML.parse(File.read(config_path)) vaults_data = yaml_data["filesystems"].as_a - vaults_data.each do |vault_data| - type = vault_data["type"].as_s - name = vault_data["name"].as_s - encrypted_path = vault_data["encrypted_path"].as_s - @filesystems << GoCryptFS.new(name, encrypted_path, "#{name}.Open") + vaults_data.each do |filesystem_data| + type = filesystem_data["type"].as_s + name = filesystem_data["name"].as_s + # encrypted_path = filesystem_data["encrypted_path"].as_s + @filesystems << Filesystem.from_yaml(filesystem_data.to_yaml) + # @filesystems << Filesystem.new(name, encrypted_path, "#{name}.Open") end end end diff --git a/src/filesystems.cr b/src/filesystems.cr new file mode 100644 index 0000000..fb69044 --- /dev/null +++ b/src/filesystems.cr @@ -0,0 +1,3 @@ + +require "./filesystems/gocryptfs" +require "./filesystems/filesystem" diff --git a/src/filesystems/filesystem.cr b/src/filesystems/filesystem.cr new file mode 100644 index 0000000..e99d4fe --- /dev/null +++ b/src/filesystems/filesystem.cr @@ -0,0 +1,38 @@ + + +require "yaml" + +module GX + abstract class Filesystem + include YAML::Serializable + + use_yaml_discriminator "type", { + gocryptfs: GoCryptFS, + sshfs: SshFS + } + + property type : String + end + + module GenericFilesystem + def unmount + system("fusermount -u #{mount_dir.shellescape}") + puts "Filesystem #{name} is now closed.".colorize(:green) + end + + def mount(&block) + Dir.mkdir_p(mount_dir) unless Dir.exists?(mount_dir) + if mounted? + puts "Already mounted. Skipping.".colorize(:yellow) + return + end + + yield + + puts "Filesystem #{name} is now available on #{mount_dir}".colorize(:green) + end + end +end + +require "./gocryptfs" +require "./sshfs" diff --git a/src/filesystems/gocryptfs.cr b/src/filesystems/gocryptfs.cr index 54c99ce..d28a825 100644 --- a/src/filesystems/gocryptfs.cr +++ b/src/filesystems/gocryptfs.cr @@ -1,14 +1,19 @@ require "shellwords" +require "./filesystem" module GX - class GoCryptFS - getter name : String - getter encrypted_path : String - getter mount_dir : String + class GoCryptFS < Filesystem + getter name : String = "" + getter encrypted_path : String = "" - def initialize(@name, @encrypted_path, mount_name : String) + @[YAML::Field(key: "mount_dir", ignore: true)] + getter mount_dir : String = "" + + include GenericFilesystem + + def after_initialize() home_dir = ENV["HOME"] || raise "Home directory not found" - @mount_dir = File.join(home_dir, "mnt/#{mount_name}") + @mount_dir = File.join(home_dir, "mnt/#{@name}") end def mounted? : Bool @@ -16,26 +21,22 @@ module GX end def mount - Dir.mkdir_p(mount_dir) unless Dir.exists?(mount_dir) - - if mounted? - puts "Already mounted. Skipping.".colorize(:yellow) - else + super do input = STDIN output = STDOUT error = STDERR - process = Process.new("gocryptfs", ["-idle", "15m", encrypted_path, mount_dir], input: input, output: output, error: error) + process = Process.new( + "gocryptfs", + ["-idle", "15m", encrypted_path, mount_dir], + input: input, + output: output, + error: error + ) unless process.wait.success? puts "Error mounting the vault".colorize(:red) return end - puts "GoCryptFS #{name} is now available on #{mount_dir}".colorize(:green) end end - - def unmount - system("fusermount -u #{mount_dir.shellescape}") - puts "GoCryptFS #{name} is now closed.".colorize(:green) - end end end diff --git a/src/filesystems/sshfs.cr b/src/filesystems/sshfs.cr new file mode 100644 index 0000000..7e12b7c --- /dev/null +++ b/src/filesystems/sshfs.cr @@ -0,0 +1,44 @@ +require "shellwords" +require "./filesystem" + +module GX + class SshFS < Filesystem + getter name : String = "" + getter remote_path : String = "" + getter remote_user : String = "" + getter remote_host : String = "" + + @[YAML::Field(key: "mount_dir", ignore: true)] + getter mount_dir : String = "" + + include GenericFilesystem + + def after_initialize() + home_dir = ENV["HOME"] || raise "Home directory not found" + @mount_dir = File.join(home_dir, "mnt/#{@name}") + end + + def mounted? : Bool + `mount`.includes?("#{remote_user}@#{remote_host}:#{remote_path} on #{mount_dir}") + end + + def mount + super do + input = STDIN + output = STDOUT + error = STDERR + process = Process.new( + "sshfs", + ["#{remote_user}@#{remote_host}:#{remote_path}", mount_dir], + input: input, + output: output, + error: error + ) + unless process.wait.success? + puts "Error mounting the filesystem".colorize(:red) + return + end + end + end + end +end