Skip to content

Commit

Permalink
[apache#6320] feat (gvfs-fuse): Support mount and umount command for …
Browse files Browse the repository at this point in the history
…gvfs-fuse command line tools (apache#6321)

### What changes were proposed in this pull request?

1. Support mount and umount command
2. Make mount create a daemon process to run background


### Why are the changes needed?

Fix: apache#6320 

### Does this PR introduce _any_ user-facing change?

No

### How was this patch tested?

IT

---------

Co-authored-by: Qiming Teng <[email protected]>
  • Loading branch information
2 people authored and youngyjd committed Feb 14, 2025
1 parent e618562 commit ec9ba1a
Show file tree
Hide file tree
Showing 12 changed files with 328 additions and 57 deletions.
2 changes: 2 additions & 0 deletions clients/filesystem-fuse/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions clients/filesystem-fuse/conf/gvfs_fuse.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
file_mask = 0o600
dir_mask = 0o700
fs_type = "memory"
data_path = "target/gvfs-fuse"

[fuse.properties]

Expand Down
59 changes: 59 additions & 0 deletions clients/filesystem-fuse/src/command_args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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 = "0.1",
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 = "The URI of the GVFS fileset, like gvfs://fileset/my_catalog/my_schema/my_fileset"
)]
fileset_location: String,

#[arg(short, long, help = "Path to the configuration file")]
config: Option<String>,

#[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,
},
}
72 changes: 54 additions & 18 deletions clients/filesystem-fuse/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ use serde::Deserialize;
use std::collections::HashMap;
use std::fs;

// FuseConfig
pub(crate) const CONF_FUSE_FILE_MASK: ConfigEntity<u32> = ConfigEntity::new(
FuseConfig::MODULE_NAME,
"file_mask",
Expand All @@ -45,20 +46,36 @@ 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<u32> = ConfigEntity::new(
FilesystemConfig::MODULE_NAME,
"block_size",
"The block size of the gvfs fuse filesystem",
4096,
);

// GravitinoConfig
pub(crate) const CONF_GRAVITINO_URI: ConfigEntity<&'static str> = ConfigEntity::new(
GravitinoConfig::MODULE_NAME,
"uri",
Expand Down Expand Up @@ -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 }
}
}
Expand Down Expand Up @@ -205,38 +232,39 @@ 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<AppConfig> {
pub fn from_file(config_file_path: Option<String>) -> GvfsResult<AppConfig> {
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
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
CONF_FUSE_CONFIG_FILE_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);
Expand Down Expand Up @@ -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<String, String>,
}
Expand Down Expand Up @@ -302,9 +334,11 @@ 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.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");
Expand All @@ -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, "");
Expand Down
3 changes: 2 additions & 1 deletion clients/filesystem-fuse/src/fuse_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}
Expand Down
3 changes: 3 additions & 0 deletions clients/filesystem-fuse/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Loading

0 comments on commit ec9ba1a

Please sign in to comment.