From 909fe5cd6cb8281627c17ca81a55218d532cd4f5 Mon Sep 17 00:00:00 2001 From: yuhui Date: Thu, 16 Jan 2025 11:29:35 +0800 Subject: [PATCH 1/6] gvfs fuse Support daemonize --- clients/filesystem-fuse/Cargo.toml | 2 + clients/filesystem-fuse/src/command_args.rs | 57 ++++++ clients/filesystem-fuse/src/config.rs | 12 +- clients/filesystem-fuse/src/fuse_server.rs | 3 +- clients/filesystem-fuse/src/main.rs | 183 +++++++++++++++--- clients/filesystem-fuse/src/s3_filesystem.rs | 2 +- .../filesystem-fuse/tests/bin/gvfs_fuse.sh | 6 +- clients/filesystem-fuse/tests/fuse_test.rs | 2 +- 8 files changed, 224 insertions(+), 43 deletions(-) create mode 100644 clients/filesystem-fuse/src/command_args.rs diff --git a/clients/filesystem-fuse/Cargo.toml b/clients/filesystem-fuse/Cargo.toml index 3760bd5285f..e6006836894 100644 --- a/clients/filesystem-fuse/Cargo.toml +++ b/clients/filesystem-fuse/Cargo.toml @@ -35,7 +35,9 @@ name = "gvfs_fuse" [dependencies] async-trait = "0.1" bytes = "1.6.0" +clap = { version = "4.5.24", features = ["derive"] } config = "0.13" +daemonize = "0.5.0" dashmap = "6.1.0" fuse3 = { version = "0.8.1", "features" = ["tokio-runtime", "unprivileged"] } futures-util = "0.3.30" diff --git a/clients/filesystem-fuse/src/command_args.rs b/clients/filesystem-fuse/src/command_args.rs new file mode 100644 index 00000000000..4e64eefa59b --- /dev/null +++ b/clients/filesystem-fuse/src/command_args.rs @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +use clap::{Parser, Subcommand}; + +#[derive(Parser, Debug)] +#[command( + name = "gvfs-fuse", + version = "1.0", + about = "A FUSE-based file system client" +)] +pub(crate) struct Arguments { + #[command(subcommand)] + pub(crate) command: Commands, +} + +#[derive(Subcommand, Debug)] +pub(crate) enum Commands { + Mount { + #[arg(help = "Mount point for the filesystem")] + mount_point: String, + + #[arg(help = "Location of GVFS fileset URI")] + location: String, + + #[arg(short, long)] + config: Option, + + #[arg(short, long, help = "Debug level", default_value_t = 0)] + debug: u8, + + #[arg(short, long, default_value_t = false, help = "Run in foreground")] + foreground: bool, + }, + Umount { + #[arg(help = "Mount point to umount")] + mount_point: String, + + #[arg(short, long, help = "Force umount")] + force: bool, + }, +} diff --git a/clients/filesystem-fuse/src/config.rs b/clients/filesystem-fuse/src/config.rs index 17908fd08fc..96b720b4ed4 100644 --- a/clients/filesystem-fuse/src/config.rs +++ b/clients/filesystem-fuse/src/config.rs @@ -205,19 +205,19 @@ impl AppConfig { .unwrap_or_else(|e| panic!("Failed to set default for {}: {}", entity.name, e)) } - pub fn from_file(config_file_path: Option<&str>) -> GvfsResult { + pub fn from_file(config_file_path: Option) -> GvfsResult { let builder = Self::crete_default_config_builder(); let config_path = { if config_file_path.is_some() { let path = config_file_path.unwrap(); //check config file exists - if fs::metadata(path).is_err() { + if fs::metadata(&path).is_err() { return Err( ConfigNotFound.to_error("The configuration file not found".to_string()) ); } - info!("Use configuration file: {}", path); + info!("Use configuration file: {}", &path); path } else { //use default config @@ -232,11 +232,11 @@ impl AppConfig { CONF_FUSE_CONFIG_PATH.default ); } - CONF_FUSE_CONFIG_PATH.default + CONF_FUSE_CONFIG_PATH.default.to_string() } }; let config = builder - .add_source(config::File::with_name(config_path).required(true)) + .add_source(config::File::with_name(&config_path).required(true)) .build(); if let Err(e) = config { let msg = format!("Failed to build configuration: {}", e); @@ -302,7 +302,7 @@ mod test { #[test] fn test_config_from_file() { - let config = AppConfig::from_file(Some("tests/conf/config_test.toml")).unwrap(); + let config = AppConfig::from_file(Some("tests/conf/config_test.toml".to_string())).unwrap(); assert_eq!(config.fuse.file_mask, 0o644); assert_eq!(config.fuse.dir_mask, 0o755); assert_eq!(config.filesystem.block_size, 8192); diff --git a/clients/filesystem-fuse/src/fuse_server.rs b/clients/filesystem-fuse/src/fuse_server.rs index a059686e16c..0e8bd518334 100644 --- a/clients/filesystem-fuse/src/fuse_server.rs +++ b/clients/filesystem-fuse/src/fuse_server.rs @@ -20,6 +20,7 @@ use crate::utils::GvfsResult; use fuse3::raw::{Filesystem, Session}; use fuse3::MountOptions; use log::{error, info}; +use std::path::Path; use std::process::exit; use std::sync::Arc; use tokio::select; @@ -46,7 +47,7 @@ impl FuseServer { /// Starts the FUSE filesystem and blocks until it is stopped. pub async fn start(&self, fuse_fs: impl Filesystem + Sync + 'static) -> GvfsResult<()> { //check if the mount point exists - if !std::path::Path::new(&self.mount_point).exists() { + if !Path::new(&self.mount_point).exists() { error!("Mount point {} does not exist", self.mount_point); exit(libc::ENOENT); } diff --git a/clients/filesystem-fuse/src/main.rs b/clients/filesystem-fuse/src/main.rs index 3534e033465..e86e943cf16 100644 --- a/clients/filesystem-fuse/src/main.rs +++ b/clients/filesystem-fuse/src/main.rs @@ -16,49 +16,170 @@ * specific language governing permissions and limitations * under the License. */ -use fuse3::Errno; +mod command_args; + +use crate::command_args::Commands; +use clap::Parser; +use daemonize::Daemonize; use gvfs_fuse::config::AppConfig; use gvfs_fuse::{gvfs_mount, gvfs_unmount}; use log::{error, info}; +use std::fs::OpenOptions; +use std::path::Path; +use std::process::{exit, Command}; +use std::{env, io}; +use tokio::runtime::Runtime; use tokio::signal; +use tokio::signal::unix::{signal, SignalKind}; -#[tokio::main] -async fn main() -> fuse3::Result<()> { - tracing_subscriber::fmt().init(); +fn make_daemon() { + let log_file_name = "/tmp/gvfs-fuse.log"; + let log_file = OpenOptions::new() + .create(true) + .append(true) + .open(log_file_name) + .unwrap(); + let log_err_file = OpenOptions::new().write(true).open(log_file_name).unwrap(); - // todo need inmprove the args parsing - let args: Vec = std::env::args().collect(); - let (mount_point, mount_from, config_path) = match args.len() { - 4 => (args[1].clone(), args[2].clone(), args[3].clone()), - _ => { - error!("Usage: {} ", args[0]); - return Err(Errno::from(libc::EINVAL)); - } - }; + let cwd = env::current_dir(); + if let Err(e) = cwd { + error!("Error getting current directory: {}", e); + return; + } + let cwd = cwd.unwrap(); + + let daemonize = Daemonize::new() + .pid_file("/tmp/gvfs-fuse.pid") + .chown_pid_file(true) + .working_directory(&cwd) + .stdout(log_file) + .stderr(log_err_file); - //todo(read config file from args) - let config = AppConfig::from_file(Some(&config_path)); - if let Err(e) = &config { - error!("Failed to load config: {:?}", e); - return Err(Errno::from(libc::EINVAL)); + match daemonize.start() { + Ok(_) => info!("Gvfs-fuse Daemon started successfully"), + Err(e) => { + error!("Gvfs-fuse Daemon failed to start: {:?}", e); + exit(-1) + } } - let config = config.unwrap(); - let handle = tokio::spawn(async move { - let result = gvfs_mount(&mount_point, &mount_from, &config).await; - if let Err(e) = result { - error!("Failed to mount gvfs: {:?}", e); - return Err(Errno::from(libc::EINVAL)); +} + +fn mount_fuse(config: AppConfig, mount_point: String, target: String) -> io::Result<()> { + let rt = Runtime::new().unwrap(); + rt.block_on(async { + let handle = tokio::spawn(async move { + let result = gvfs_mount(&mount_point, &target, &config).await; + if let Err(e) = result { + error!("Failed to mount gvfs: {:?}", e); + return Err(io::Error::from(io::ErrorKind::InvalidInput)); + } + Ok(()) + }); + + let mut term_signal = signal(SignalKind::terminate())?; + tokio::select! { + _ = handle => {} + _ = signal::ctrl_c() => { + info!("Received Ctrl+C, unmounting gvfs...") + } + _ = term_signal.recv()=> { + info!("Received SIGTERM, unmounting gvfs...") + } } + + let _ = gvfs_unmount().await; Ok(()) - }); + }) +} + +#[cfg(target_os = "macos")] +fn do_umount(mp: &str, force: bool) -> std::io::Result<()> { + let cmd_result = if force { + Command::new("umount").arg("-f").arg(mp).output() + } else { + Command::new("umount").arg(mp).output() + }; - tokio::select! { - _ = handle => {} - _ = signal::ctrl_c() => { - info!("Received Ctrl+C, unmounting gvfs..."); + handle_command_result(cmd_result) +} + +#[cfg(target_os = "linux")] +fn do_umount(mp: &str, force: bool) -> std::io::Result<()> { + let cmd_result = + if Path::new("/bin/fusermount").exists() || Path::new("/usr/bin/fusermount").exists() { + if force { + Command::new("fusermount").arg("-uz").arg(mp).output() + } else { + Command::new("fusermount").arg("-u").arg(mp).output() + } + } else if force { + Command::new("umount").arg("-l").arg(mp).output() + } else { + Command::new("umount").arg(mp).output() + }; + + handle_command_result(cmd_result) +} + +fn handle_command_result(cmd_result: io::Result) -> io::Result<()> { + match cmd_result { + Ok(output) => { + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + Err(io::Error::new(io::ErrorKind::Other, stderr.to_string())) + } else { + Ok(()) + } } + Err(e) => Err(e), } +} - let _ = gvfs_unmount().await; - Ok(()) +#[cfg(not(any(target_os = "macos", target_os = "linux")))] +fn do_umount(_mp: &str, _force: bool) -> std::io::Result<()> { + Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!("OS {} is not supported", env::consts::OS), + )) +} + +fn main() -> Result<(), i32> { + tracing_subscriber::fmt().init(); + let args = command_args::Arguments::parse(); + match args.command { + Commands::Mount { + mount_point, + location, + config, + debug: _, + foreground, + } => { + let app_config = AppConfig::from_file(config); + if let Err(e) = &app_config { + error!("Failed to load config: {:?}", e); + return Err(-1); + }; + let app_config = app_config.unwrap(); + let result = if foreground { + mount_fuse(app_config, mount_point, location) + } else { + make_daemon(); + mount_fuse(app_config, mount_point, location) + }; + + if let Err(e) = result { + error!("Failed to mount gvfs: {:?}", e.to_string()); + return Err(-1); + }; + Ok(()) + } + Commands::Umount { mount_point, force } => { + let result = do_umount(&mount_point, force); + if let Err(e) = result { + error!("Failed to unmount gvfs: {:?}", e.to_string()); + return Err(-1); + }; + Ok(()) + } + } } diff --git a/clients/filesystem-fuse/src/s3_filesystem.rs b/clients/filesystem-fuse/src/s3_filesystem.rs index 35a091b3fe1..e397d31ccf3 100644 --- a/clients/filesystem-fuse/src/s3_filesystem.rs +++ b/clients/filesystem-fuse/src/s3_filesystem.rs @@ -255,7 +255,7 @@ pub(crate) mod tests { config_file_name = source_file_name; } - AppConfig::from_file(Some(config_file_name)).unwrap() + AppConfig::from_file(Some(config_file_name.to_string())).unwrap() } #[tokio::test] diff --git a/clients/filesystem-fuse/tests/bin/gvfs_fuse.sh b/clients/filesystem-fuse/tests/bin/gvfs_fuse.sh index e706d8e2c0d..b6a04f3b2a0 100644 --- a/clients/filesystem-fuse/tests/bin/gvfs_fuse.sh +++ b/clients/filesystem-fuse/tests/bin/gvfs_fuse.sh @@ -52,14 +52,14 @@ start_gvfs_fuse() { make build echo "Starting gvfs-fuse-daemon" - $CLIENT_FUSE_DIR/target/debug/gvfs-fuse $MOUNT_DIR $MOUNT_FROM_LOCATION $TEST_CONFIG_FILE > \ + $CLIENT_FUSE_DIR/target/debug/gvfs-fuse mount $MOUNT_DIR $MOUNT_FROM_LOCATION -c $TEST_CONFIG_FILE -f > \ $CLIENT_FUSE_DIR/target/debug/fuse.log 2>&1 & check_gvfs_fuse_ready cd - } stop_gvfs_fuse() { - # Stop the gvfs-fuse process if it's running - pkill -INT gvfs-fuse || true + # Unmount the gvfs-fuse + $CLIENT_FUSE_DIR/target/debug/gvfs-fuse umount $MOUNT_DIR echo "Stopping gvfs-fuse-daemon" } \ No newline at end of file diff --git a/clients/filesystem-fuse/tests/fuse_test.rs b/clients/filesystem-fuse/tests/fuse_test.rs index 41e385c49f1..29f47a3dae9 100644 --- a/clients/filesystem-fuse/tests/fuse_test.rs +++ b/clients/filesystem-fuse/tests/fuse_test.rs @@ -42,7 +42,7 @@ impl FuseTest { info!("Start gvfs fuse server"); let mount_point = self.mount_point.clone(); - let config = AppConfig::from_file(Some("tests/conf/gvfs_fuse_memory.toml")) + let config = AppConfig::from_file(Some("tests/conf/gvfs_fuse_memory.toml".to_string())) .expect("Failed to load config"); self.runtime.spawn(async move { let result = gvfs_mount(&mount_point, "", &config).await; From 8a7c9da4c4ac95f0bd5cb68ec39db703975b6d10 Mon Sep 17 00:00:00 2001 From: yuhui Date: Mon, 20 Jan 2025 10:46:26 +0800 Subject: [PATCH 2/6] Fix --- clients/filesystem-fuse/src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clients/filesystem-fuse/src/main.rs b/clients/filesystem-fuse/src/main.rs index e86e943cf16..12e7f43a0a1 100644 --- a/clients/filesystem-fuse/src/main.rs +++ b/clients/filesystem-fuse/src/main.rs @@ -25,7 +25,6 @@ use gvfs_fuse::config::AppConfig; use gvfs_fuse::{gvfs_mount, gvfs_unmount}; use log::{error, info}; use std::fs::OpenOptions; -use std::path::Path; use std::process::{exit, Command}; use std::{env, io}; use tokio::runtime::Runtime; @@ -65,7 +64,7 @@ fn make_daemon() { } fn mount_fuse(config: AppConfig, mount_point: String, target: String) -> io::Result<()> { - let rt = Runtime::new().unwrap(); + let rt = Runtime::new()?; rt.block_on(async { let handle = tokio::spawn(async move { let result = gvfs_mount(&mount_point, &target, &config).await; From 0a7a3af36ece0fd2cc3fda3e426491aac2189fb6 Mon Sep 17 00:00:00 2001 From: yuhui Date: Mon, 20 Jan 2025 15:19:13 +0800 Subject: [PATCH 3/6] Fix log filename and pid filename --- clients/filesystem-fuse/conf/gvfs_fuse.toml | 1 + clients/filesystem-fuse/src/config.rs | 62 +++++++++++++++---- clients/filesystem-fuse/src/lib.rs | 3 + clients/filesystem-fuse/src/main.rs | 45 +++++++++----- .../tests/conf/config_test.toml | 2 + .../tests/conf/gvfs_fuse_s3.toml | 1 + 6 files changed, 85 insertions(+), 29 deletions(-) diff --git a/clients/filesystem-fuse/conf/gvfs_fuse.toml b/clients/filesystem-fuse/conf/gvfs_fuse.toml index 4bde0e9e1bd..27e52fc7d57 100644 --- a/clients/filesystem-fuse/conf/gvfs_fuse.toml +++ b/clients/filesystem-fuse/conf/gvfs_fuse.toml @@ -20,6 +20,7 @@ file_mask = 0o600 dir_mask = 0o700 fs_type = "memory" +data_path = "target/gvfs-fuse" [fuse.properties] diff --git a/clients/filesystem-fuse/src/config.rs b/clients/filesystem-fuse/src/config.rs index 96b720b4ed4..890cb7d31dc 100644 --- a/clients/filesystem-fuse/src/config.rs +++ b/clients/filesystem-fuse/src/config.rs @@ -24,6 +24,7 @@ use serde::Deserialize; use std::collections::HashMap; use std::fs; +// FuseConfig pub(crate) const CONF_FUSE_FILE_MASK: ConfigEntity = ConfigEntity::new( FuseConfig::MODULE_NAME, "file_mask", @@ -45,13 +46,28 @@ pub(crate) const CONF_FUSE_FS_TYPE: ConfigEntity<&'static str> = ConfigEntity::n "memory", ); -pub(crate) const CONF_FUSE_CONFIG_PATH: ConfigEntity<&'static str> = ConfigEntity::new( +pub(crate) const CONF_FUSE_CONFIG_FILE_PATH: ConfigEntity<&'static str> = ConfigEntity::new( FuseConfig::MODULE_NAME, "config_path", "The path of the FUSE configuration file", - "/etc/gvfs/gvfs.toml", + "/etc/gvfs-fuse/config.toml", ); +pub(crate) const CONF_FUSE_DATA_DIR: ConfigEntity<&'static str> = ConfigEntity::new( + FuseConfig::MODULE_NAME, + "data_dir", + "The data path of GVFS FUSE", + "/var/data/gvfs-fuse", +); + +pub(crate) const CONF_FUSE_LOG_DIR: ConfigEntity<&'static str> = ConfigEntity::new( + FuseConfig::MODULE_NAME, + "log_dir", + "The log path of GVFS FUSE", + "logs", //relative to the data path +); + +// FilesystemConfig pub(crate) const CONF_FILESYSTEM_BLOCK_SIZE: ConfigEntity = ConfigEntity::new( FilesystemConfig::MODULE_NAME, "block_size", @@ -59,6 +75,7 @@ pub(crate) const CONF_FILESYSTEM_BLOCK_SIZE: ConfigEntity = ConfigEntity::n 4096, ); +// GravitinoConfig pub(crate) const CONF_GRAVITINO_URI: ConfigEntity<&'static str> = ConfigEntity::new( GravitinoConfig::MODULE_NAME, "uri", @@ -125,22 +142,32 @@ impl Default for DefaultConfig { ConfigValue::String(CONF_FUSE_FS_TYPE), ); configs.insert( - Self::compose_key(CONF_FUSE_CONFIG_PATH), - ConfigValue::String(CONF_FUSE_CONFIG_PATH), + Self::compose_key(CONF_FUSE_CONFIG_FILE_PATH), + ConfigValue::String(CONF_FUSE_CONFIG_FILE_PATH), ); configs.insert( - Self::compose_key(CONF_GRAVITINO_URI), - ConfigValue::String(CONF_GRAVITINO_URI), + Self::compose_key(CONF_FUSE_DATA_DIR), + ConfigValue::String(CONF_FUSE_DATA_DIR), ); configs.insert( - Self::compose_key(CONF_GRAVITINO_METALAKE), - ConfigValue::String(CONF_GRAVITINO_METALAKE), + Self::compose_key(CONF_FUSE_LOG_DIR), + ConfigValue::String(CONF_FUSE_LOG_DIR), ); + configs.insert( Self::compose_key(CONF_FILESYSTEM_BLOCK_SIZE), ConfigValue::U32(CONF_FILESYSTEM_BLOCK_SIZE), ); + configs.insert( + Self::compose_key(CONF_GRAVITINO_URI), + ConfigValue::String(CONF_GRAVITINO_URI), + ); + configs.insert( + Self::compose_key(CONF_GRAVITINO_METALAKE), + ConfigValue::String(CONF_GRAVITINO_METALAKE), + ); + DefaultConfig { configs } } } @@ -220,19 +247,20 @@ impl AppConfig { info!("Use configuration file: {}", &path); path } else { - //use default config - if fs::metadata(CONF_FUSE_CONFIG_PATH.default).is_err() { + if fs::metadata(CONF_FUSE_CONFIG_FILE_PATH.default).is_err() { + //use default config warn!( "The default configuration file is not found, using the default configuration" ); return Ok(AppConfig::default()); } else { + //use the default configuration file warn!( "Using the default config file {}", - CONF_FUSE_CONFIG_PATH.default + CONF_FUSE_CONFIG_FILE_PATH.default ); } - CONF_FUSE_CONFIG_PATH.default.to_string() + CONF_FUSE_CONFIG_FILE_PATH.default.to_string() } }; let config = builder @@ -265,7 +293,11 @@ pub struct FuseConfig { #[serde(default)] pub fs_type: String, #[serde(default)] - pub config_path: String, + pub config_file_path: String, + #[serde(default)] + pub data_dir: String, + #[serde(default)] + pub log_dir: String, #[serde(default)] pub properties: HashMap, } @@ -305,6 +337,8 @@ mod test { let config = AppConfig::from_file(Some("tests/conf/config_test.toml".to_string())).unwrap(); assert_eq!(config.fuse.file_mask, 0o644); assert_eq!(config.fuse.dir_mask, 0o755); + assert_eq!(config.fuse.data_dir, "/target/gvfs-fuse"); + assert_eq!(config.fuse.log_dir, "/target/gvfs-fuse/logs"); assert_eq!(config.filesystem.block_size, 8192); assert_eq!(config.gravitino.uri, "http://localhost:8090"); assert_eq!(config.gravitino.metalake, "test"); @@ -323,6 +357,8 @@ mod test { let config = AppConfig::default(); assert_eq!(config.fuse.file_mask, 0o600); assert_eq!(config.fuse.dir_mask, 0o700); + assert_eq!(config.fuse.data_dir, "/var/data/gvfs-fuse"); + assert_eq!(config.fuse.log_dir, "logs"); assert_eq!(config.filesystem.block_size, 4096); assert_eq!(config.gravitino.uri, "http://localhost:8090"); assert_eq!(config.gravitino.metalake, ""); diff --git a/clients/filesystem-fuse/src/lib.rs b/clients/filesystem-fuse/src/lib.rs index 41a9a5335d5..65cd878f34d 100644 --- a/clients/filesystem-fuse/src/lib.rs +++ b/clients/filesystem-fuse/src/lib.rs @@ -49,6 +49,9 @@ macro_rules! test_enable_with { pub const RUN_TEST_WITH_S3: &str = "RUN_TEST_WITH_S3"; pub const RUN_TEST_WITH_FUSE: &str = "RUN_TEST_WITH_FUSE"; +pub const LOG_FILE_NAME: &str = "gvfs-fuse.log"; +pub const PID_FILE_NAME: &str = "gvfs-fuse.pid"; + pub async fn gvfs_mount(mount_to: &str, mount_from: &str, config: &AppConfig) -> GvfsResult<()> { gvfs_fuse::mount(mount_to, mount_from, config).await } diff --git a/clients/filesystem-fuse/src/main.rs b/clients/filesystem-fuse/src/main.rs index 12e7f43a0a1..536c8365228 100644 --- a/clients/filesystem-fuse/src/main.rs +++ b/clients/filesystem-fuse/src/main.rs @@ -22,35 +22,43 @@ use crate::command_args::Commands; use clap::Parser; use daemonize::Daemonize; use gvfs_fuse::config::AppConfig; -use gvfs_fuse::{gvfs_mount, gvfs_unmount}; +use gvfs_fuse::{gvfs_mount, gvfs_unmount, LOG_FILE_NAME, PID_FILE_NAME}; use log::{error, info}; -use std::fs::OpenOptions; +use std::fs::{create_dir_all, OpenOptions}; +use std::io; +use std::path::Path; use std::process::{exit, Command}; -use std::{env, io}; use tokio::runtime::Runtime; use tokio::signal; use tokio::signal::unix::{signal, SignalKind}; -fn make_daemon() { - let log_file_name = "/tmp/gvfs-fuse.log"; +fn make_daemon(config: &AppConfig) -> io::Result<()> { + let data_dir_name = Path::new(&config.fuse.data_dir).to_path_buf(); + if !data_dir_name.exists() { + Err(io::Error::new( + io::ErrorKind::NotFound, + format!("Data directory {} not found", &config.fuse.data_dir), + ))? + }; + + let log_dir_name = data_dir_name.join(&config.fuse.log_dir); + if !log_dir_name.exists() { + create_dir_all(&log_dir_name)? + }; + let log_file_name = Path::new(&config.fuse.log_dir).join(LOG_FILE_NAME); let log_file = OpenOptions::new() .create(true) .append(true) - .open(log_file_name) + .open(&log_file_name) .unwrap(); - let log_err_file = OpenOptions::new().write(true).open(log_file_name).unwrap(); + let log_err_file = OpenOptions::new().append(true).open(log_file_name).unwrap(); - let cwd = env::current_dir(); - if let Err(e) = cwd { - error!("Error getting current directory: {}", e); - return; - } - let cwd = cwd.unwrap(); + let pid_file_name = data_dir_name.join(PID_FILE_NAME); let daemonize = Daemonize::new() - .pid_file("/tmp/gvfs-fuse.pid") + .pid_file(pid_file_name) .chown_pid_file(true) - .working_directory(&cwd) + .working_directory(&data_dir_name) .stdout(log_file) .stderr(log_err_file); @@ -61,6 +69,7 @@ fn make_daemon() { exit(-1) } } + Ok(()) } fn mount_fuse(config: AppConfig, mount_point: String, target: String) -> io::Result<()> { @@ -162,7 +171,11 @@ fn main() -> Result<(), i32> { let result = if foreground { mount_fuse(app_config, mount_point, location) } else { - make_daemon(); + let result = make_daemon(&app_config); + if let Err(e) = result { + error!("Failed to daemonize: {:?}", e); + return Err(-1); + }; mount_fuse(app_config, mount_point, location) }; diff --git a/clients/filesystem-fuse/tests/conf/config_test.toml b/clients/filesystem-fuse/tests/conf/config_test.toml index 524e0aa94fb..e7cbf02c7f3 100644 --- a/clients/filesystem-fuse/tests/conf/config_test.toml +++ b/clients/filesystem-fuse/tests/conf/config_test.toml @@ -20,6 +20,8 @@ file_mask= 0o644 dir_mask= 0o755 fs_type = "memory" +data_dir = "/target/gvfs-fuse" +log_dir = "/target/gvfs-fuse/logs" [fuse.properties] key1 = "value1" diff --git a/clients/filesystem-fuse/tests/conf/gvfs_fuse_s3.toml b/clients/filesystem-fuse/tests/conf/gvfs_fuse_s3.toml index d0ff8e5ddec..3735b2a20da 100644 --- a/clients/filesystem-fuse/tests/conf/gvfs_fuse_s3.toml +++ b/clients/filesystem-fuse/tests/conf/gvfs_fuse_s3.toml @@ -20,6 +20,7 @@ file_mask= 0o600 dir_mask= 0o700 fs_type = "gvfs" +data_path = "target/debug/gvfs-fuse" [fuse.properties] key1 = "value1" From 387fa9c8448dc28fed45802a241acb940ea49e8c Mon Sep 17 00:00:00 2001 From: yuhui Date: Mon, 20 Jan 2025 15:32:24 +0800 Subject: [PATCH 4/6] Fix --- clients/filesystem-fuse/src/main.rs | 37 ++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/clients/filesystem-fuse/src/main.rs b/clients/filesystem-fuse/src/main.rs index 536c8365228..9b9aea394c8 100644 --- a/clients/filesystem-fuse/src/main.rs +++ b/clients/filesystem-fuse/src/main.rs @@ -24,15 +24,15 @@ use daemonize::Daemonize; use gvfs_fuse::config::AppConfig; use gvfs_fuse::{gvfs_mount, gvfs_unmount, LOG_FILE_NAME, PID_FILE_NAME}; use log::{error, info}; -use std::fs::{create_dir_all, OpenOptions}; +use std::fs::{create_dir, OpenOptions}; use std::io; use std::path::Path; -use std::process::{exit, Command}; +use std::process::Command; use tokio::runtime::Runtime; use tokio::signal; use tokio::signal::unix::{signal, SignalKind}; -fn make_daemon(config: &AppConfig) -> io::Result<()> { +fn init_work_dirs(config: &AppConfig, mount_point: &str) -> io::Result<()> { let data_dir_name = Path::new(&config.fuse.data_dir).to_path_buf(); if !data_dir_name.exists() { Err(io::Error::new( @@ -41,11 +41,23 @@ fn make_daemon(config: &AppConfig) -> io::Result<()> { ))? }; + let mount_point_name = data_dir_name.join(mount_point); + if !mount_point_name.exists() { + create_dir(&mount_point_name)? + }; + let log_dir_name = data_dir_name.join(&config.fuse.log_dir); if !log_dir_name.exists() { - create_dir_all(&log_dir_name)? + create_dir(&log_dir_name)? }; - let log_file_name = Path::new(&config.fuse.log_dir).join(LOG_FILE_NAME); + + Ok(()) +} + +fn make_daemon(config: &AppConfig) -> io::Result<()> { + let data_dir_name = Path::new(&config.fuse.data_dir); + let log_dir_name = data_dir_name.join(&config.fuse.log_dir); + let log_file_name = log_dir_name.join(LOG_FILE_NAME); let log_file = OpenOptions::new() .create(true) .append(true) @@ -58,15 +70,17 @@ fn make_daemon(config: &AppConfig) -> io::Result<()> { let daemonize = Daemonize::new() .pid_file(pid_file_name) .chown_pid_file(true) - .working_directory(&data_dir_name) + .working_directory(data_dir_name) .stdout(log_file) .stderr(log_err_file); match daemonize.start() { Ok(_) => info!("Gvfs-fuse Daemon started successfully"), Err(e) => { - error!("Gvfs-fuse Daemon failed to start: {:?}", e); - exit(-1) + return Err(io::Error::new( + io::ErrorKind::Other, + format!("Gvfs-fuse Daemon failed to start: {:?}", e), + )) } } Ok(()) @@ -168,6 +182,13 @@ fn main() -> Result<(), i32> { return Err(-1); }; let app_config = app_config.unwrap(); + + let result = init_work_dirs(&app_config, &mount_point); + if let Err(e) = result { + error!("Failed to initialize working directories: {:?}", e); + return Err(-1); + } + let result = if foreground { mount_fuse(app_config, mount_point, location) } else { From 9976988bbbd66b6c4ab3ef719ad8d15dbadc6219 Mon Sep 17 00:00:00 2001 From: Yuhui Date: Mon, 20 Jan 2025 15:34:28 +0800 Subject: [PATCH 5/6] Update clients/filesystem-fuse/src/command_args.rs Co-authored-by: Qiming Teng --- clients/filesystem-fuse/src/command_args.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/filesystem-fuse/src/command_args.rs b/clients/filesystem-fuse/src/command_args.rs index 4e64eefa59b..8db9bcef832 100644 --- a/clients/filesystem-fuse/src/command_args.rs +++ b/clients/filesystem-fuse/src/command_args.rs @@ -35,7 +35,7 @@ pub(crate) enum Commands { #[arg(help = "Mount point for the filesystem")] mount_point: String, - #[arg(help = "Location of GVFS fileset URI")] + #[arg(help = "The URI of the GVFS fileset")] location: String, #[arg(short, long)] From 9c26f361b235fed9ab192409bbdc78bd1767feae Mon Sep 17 00:00:00 2001 From: yuhui Date: Mon, 20 Jan 2025 17:40:09 +0800 Subject: [PATCH 6/6] Update --- clients/filesystem-fuse/tests/bin/gvfs_fuse.sh | 3 ++- clients/filesystem-fuse/tests/conf/gvfs_fuse_s3.toml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/clients/filesystem-fuse/tests/bin/gvfs_fuse.sh b/clients/filesystem-fuse/tests/bin/gvfs_fuse.sh index b6a04f3b2a0..3089a9a1a1f 100644 --- a/clients/filesystem-fuse/tests/bin/gvfs_fuse.sh +++ b/clients/filesystem-fuse/tests/bin/gvfs_fuse.sh @@ -52,6 +52,7 @@ start_gvfs_fuse() { make build echo "Starting gvfs-fuse-daemon" + mkdir -p target/debug/gvfs-dir $CLIENT_FUSE_DIR/target/debug/gvfs-fuse mount $MOUNT_DIR $MOUNT_FROM_LOCATION -c $TEST_CONFIG_FILE -f > \ $CLIENT_FUSE_DIR/target/debug/fuse.log 2>&1 & check_gvfs_fuse_ready @@ -62,4 +63,4 @@ stop_gvfs_fuse() { # Unmount the gvfs-fuse $CLIENT_FUSE_DIR/target/debug/gvfs-fuse umount $MOUNT_DIR echo "Stopping gvfs-fuse-daemon" -} \ No newline at end of file +} diff --git a/clients/filesystem-fuse/tests/conf/gvfs_fuse_s3.toml b/clients/filesystem-fuse/tests/conf/gvfs_fuse_s3.toml index 3735b2a20da..69a3fcaf36d 100644 --- a/clients/filesystem-fuse/tests/conf/gvfs_fuse_s3.toml +++ b/clients/filesystem-fuse/tests/conf/gvfs_fuse_s3.toml @@ -20,7 +20,7 @@ file_mask= 0o600 dir_mask= 0o700 fs_type = "gvfs" -data_path = "target/debug/gvfs-fuse" +data_dir = "target/debug/gvfs-dir" [fuse.properties] key1 = "value1"