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

Add support for passing FUSE file descriptors as mount point #1103

Merged
merged 35 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
8c25730
Add support for passing FUSE file descriptors as mount point
unexge Nov 5, 2024
bf00000
Fix Clippy warnings
unexge Nov 5, 2024
3fc4ed9
Conditionally compile Linux specific parts
unexge Nov 5, 2024
8e5b6d1
Fix imports
unexge Nov 5, 2024
94bfd66
Add long-help to mention passing file descriptors as mount points
unexge Nov 5, 2024
d586043
Fix clippy warnings
unexge Nov 5, 2024
196c126
Make sure to clear `FD_CLOEXEC` flag on FUSE fd in fork tests
unexge Nov 7, 2024
71264fd
Warn about ignored mount options
unexge Nov 7, 2024
b92d280
Add an example script
unexge Nov 7, 2024
0b27592
Remove unused import
unexge Nov 7, 2024
36420a2
Make sure to unmount pre-existing mount first
unexge Nov 7, 2024
44250a9
Add missing comments to functions
unexge Nov 8, 2024
e9e1a09
Add CHANGELOG entry
unexge Nov 8, 2024
6b0c5da
Apply suggestions from code review for `mounthelper.go`
unexge Nov 28, 2024
267a4a6
Update mountpoint-s3/CHANGELOG.md
unexge Nov 28, 2024
633493a
Update doc comments on `MountPoint`
unexge Nov 28, 2024
ef526f6
Use `false` instead of relying on `Default::default()`
unexge Nov 28, 2024
0e84b59
Explicitly drop `Mount` instead of using `let _ =`
unexge Nov 28, 2024
b650a78
Update doc comments on `mount_for_passing_fuse_fd`
unexge Nov 28, 2024
d8fa316
Add detailed message to `assert_eq`
unexge Nov 28, 2024
bf58c3a
Add more comments to `mounthelper.go` and fix formatting
unexge Nov 28, 2024
d4006e8
Move FUSE fd usage instructions to `CONFIGURATION.md`
unexge Nov 28, 2024
b099632
Rename `new_directory`/`new_fd` to `from_directory`/`from_fd`
unexge Nov 28, 2024
87e7ac2
Fix compliation error on `cache_test.rs`
unexge Nov 28, 2024
f329e8d
Update error messages and assert them in fork tests
unexge Nov 28, 2024
05152fd
Move `SessionACL` into its own variable and add a comment
unexge Nov 28, 2024
e652f8c
Use `test_matrix` in `read_test`
unexge Nov 28, 2024
3f6ae09
Allow too_many_arguments on test fn create_fuse_session
dannycjones Nov 29, 2024
cd34ed1
Use non-existent file descriptor number
unexge Dec 2, 2024
b5cc65b
Fail if unsupported mount options passed with FUSE fd mount
unexge Dec 2, 2024
a296dc0
Fix Clippy warnings
unexge Dec 2, 2024
f488132
Allow `--allow-root` and `--allow-other` with FUSE fd mount
unexge Dec 4, 2024
04ba1c9
Fix unintentional CRT submodule changes
unexge Dec 16, 2024
e7f231a
Apply suggestions from code review
unexge Dec 17, 2024
17f20dc
Fix typo in Rustdoc comment
dannycjones Dec 17, 2024
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
19 changes: 19 additions & 0 deletions doc/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,25 @@ ExecStop=/usr/bin/fusermount -u /home/ec2-user/s3-bucket-mount
WantedBy=remote-fs.target
```

### Providing a FUSE file descriptor for mounting

Mountpoint supports mounting S3 buckets at a given path, or using a provided FUSE file descriptor (only on Linux).

For directory mount points, the passed path must be an existing directory.

For FUSE file descriptors on Linux, you can specify an open FUSE file descriptor as a mount point with `/dev/fd/N` syntax.
This is useful in container environments to achieve unprivileged mounts.
In this case, the caller is responsible for the following:
1. Opening the FUSE device (`/dev/fuse`) in read-write mode to obtain a file descriptor.
2. Performing the `mount` syscall with the desired mount point, the file descriptor, and any mount options.
Mountpoint by default uses and recommends enabling `nodev`, `nosuid`, `default_permissions`, and `noatime` mount options.
See the [Linux kernel documentation](https://man7.org/linux/man-pages/man8/mount.fuse3.8.html#OPTIONS) for more details on mount options.
3. Spawning Mountpoint with the file descriptor using `/dev/fd/N` syntax as the mount point argument.
4. Closing the file descriptor in the parent process.
5. Performing the `unmount` syscall on the mount point when unmounting is desired or when the Mountpoint process terminates.

See [mounthelper.go](https://github.com/awslabs/mountpoint-s3/tree/main/examples/fuse-fd-mount-point/mounthelper.go) as an example usage of this feature.

## Caching configuration

Mountpoint can optionally cache object metadata and content to reduce cost and improve performance for repeated reads to the same file.
Expand Down
116 changes: 116 additions & 0 deletions examples/fuse-fd-mount-point/mounthelper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// An example script showing usage of FUSE file descriptor as a mount point.
//
// Example usage:
// $ go build mounthelper.go
// $ sudo /sbin/setcap 'cap_sys_admin=ep' ./mounthelper # `mount` syscall requires `CAP_SYS_ADMIN`, alternatively, `mounthelper` can be run as root
// $ ./mounthelper -mountpoint /tmp/mountpoint -bucket bucketname
// $ # Mountpoint mounted at /tmp/mountpoint until `mounthelper` is terminated with ctrl+c.
package main

import (
"flag"
"fmt"
"log"
"os"
"os/exec"
"os/signal"
"strings"
"syscall"
)

var mountPoint = flag.String("mountpoint", "", "Path to mount the filesystem at")
var bucket = flag.String("bucket", "", "S3 Bucket to mount")

func main() {
flag.Parse()

// `os.MkdirAll` will return `nil` if `mountPoint` is an already existing directory.
if err := os.MkdirAll(*mountPoint, 0644); err != nil {
log.Panicf("Failed to create target mount point %s: %v\n", *mountPoint, err)
}

// 1. Open FUSE device
const USE_DEFAULT_PERM = 0
fd, err := syscall.Open("/dev/fuse", os.O_RDWR, USE_DEFAULT_PERM)
if err != nil {
log.Panicf("Failed to open /dev/fuse: %v\n", err)
}

// Ensure to close the file descriptor in case of an error, if there are no errors,
// the file descriptor will be closed and `closeFd` will set to `false`.
closeFd := true
defer func() {
if closeFd {
err := syscall.Close(fd)
if err != nil {
log.Panicf("Failed to close fd on parent: %v\n", err)
}
}
}()

var stat syscall.Stat_t
err = syscall.Stat(*mountPoint, &stat)
if err != nil {
log.Panicf("Failed to stat mount point %s: %v\n", *mountPoint, err)
}

// 2. Perform `mount` syscall
// These mount options and flags match those typically set when using Mountpoint.
// Some are set by the underlying FUSE library.
// Mountpoint sets (correct at the time of authoring this comment):
// * `noatime` to avoid unsupported access time updates.
// * `default_permissions` to tell the Kernel to evaluate permissions itself, since Mountpoint does not currently provide any handler for FUSE `access`.
options := []string{
fmt.Sprintf("fd=%d", fd),
fmt.Sprintf("rootmode=%o", stat.Mode),
fmt.Sprintf("user_id=%d", os.Geteuid()),
fmt.Sprintf("group_id=%d", os.Getegid()),
"default_permissions",
}
var flags uintptr = syscall.MS_NOSUID | syscall.MS_NODEV | syscall.MS_NOATIME
err = syscall.Mount("mountpoint-s3", *mountPoint, "fuse", flags, strings.Join(options, ","))
if err != nil {
log.Panicf("Failed to call mount syscall: %v\n", err)
}

// 3. Define and defer call to `unmount` syscall, to be invoked once script terminates
defer func() {
err := syscall.Unmount(*mountPoint, 0)
if err != nil {
log.Printf("Failed to unmount %s: %v\n", *mountPoint, err)
} else {
log.Printf("Succesfully unmounted %s\n", *mountPoint)
}
}()

// 4. Spawn Mountpoint with the fd
mountpointCmd := exec.Command("./target/release/mount-s3",
*bucket,
fmt.Sprintf("/dev/fd/%d", fd),
// Other mount options can be added here
"--prefix=some_s3_prefix/",
"--allow-delete",
)
mountpointCmd.Stdout = os.Stdout
mountpointCmd.Stderr = os.Stderr
err = mountpointCmd.Run()
if err != nil {
log.Panicf("Failed to start Mountpoint: %v\n", err)
}

// 4. Close fd on parent
err = syscall.Close(fd)
if err != nil {
log.Panicf("Failed to close fd on parent: %v\n", err)
}
// As we expliclity closed it, no need for `defer`red close to happen.
closeFd = false

done := make(chan os.Signal, 1)
signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)

log.Print("Filesystem mounted, waiting for ctrl+c signal to terminate")
<-done

// 5. Unmounting will happen here due to `defer` in step 3.
}
7 changes: 7 additions & 0 deletions mountpoint-s3/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
## Unreleased

### New features

* Mountpoint now supports specifying an open FUSE file descriptor in place of the mount path by using the syntax `/dev/fd/N`.
See [mounthelper.go](https://github.com/awslabs/mountpoint-s3/tree/main/examples/fuse-fd-mount-point/mounthelper.go) as an example usage and see
[Configuring mount point](https://github.com/awslabs/mountpoint-s3/blob/main/doc/CONFIGURATION.md#configuring-mount-point) about more details on configuring this feature.
([#1103](https://github.com/awslabs/mountpoint-s3/pull/1103))

### Other changes

* Fix an issue where an interrupt during `readdir` syscall leads to an error. ([#965](https://github.com/awslabs/mountpoint-s3/pull/965))
Expand Down
Loading
Loading