Skip to content
rodolfo.gg
Go back

OSSFS2: installing and using it on Ubuntu 26.04, without dying in the attempt.

CC BY-NC-ND 4.0
Rodolfo González González

OSSFS2: installing and using it on Ubuntu 26.04, without dying in the attempt.

Introduction

OSSFS2 is Alibaba Cloud’s high-performance client for mounting Aliyun Object Storage Service (OSS) buckets as a local filesystem through FUSE. This guide covers the complete procedure on Ubuntu 26.04: building from source, adapting it to a modern toolchain, creating a .deb package with CPack, and managing several mounts through a systemd template unit.

flowchart TD
    A[Ubuntu 26.04] --> B[Install toolchain]
    B --> C[Clone OSSFS2]
    C --> D[Apply compatibility patch]
    D --> E[Configure CMake]
    E --> F[Build]
    F --> G[Generate DEB with CPack]
    G --> H[Install package]
    H --> I[Configure credentials or RAM Role]
    I --> J[Create systemd template unit]
    J --> K[Enable one instance per bucket]

1. Install dependencies

Terminal window
# Update the local package index.
sudo apt update
# Install the compiler, CMake, Git, patch, FUSE 3 and Debian tools.
sudo apt install \
build-essential \
cmake \
git \
patch \
pkg-config \
libfuse3-dev \
libaio-dev \
libssl-dev \
dpkg-dev \
fakeroot

Check the tools:

Terminal window
# Show the C++ compiler version.
c++ --version
# Show the CMake version.
cmake --version
# Locate the static standard library required by the project.
g++ -print-file-name=libstdc++.a

2. Download the source tree

Terminal window
# Create an administrative directory for local sources.
sudo install -d -m 0755 -o root -g root /usr/local/src
# Enter the sources directory.
cd /usr/local/src
# Clone the official repository into the ossfs subdirectory.
sudo git clone https://github.com/aliyun/ossfs.git ossfs
# Hand the tree over to the current administrative user.
sudo chown -R "$USER":"$(id -gn)" /usr/local/src/ossfs
# Enter the repository.
cd /usr/local/src/ossfs
# Record the exact commit that will be built.
git rev-parse HEAD

3. Why the patch is needed

With the modern toolchain, errors such as these may appear:

error: ‘uint64_t’ does not name a type
error: ‘sort’ is not a member of ‘std’
error: no matching function for call to ‘min(<brace-enclosed initializer list>)’

The observed causes are missing transitive inclusions:

4. And now, the patch

Terminal window
# Create a directory for local adaptations.
sudo install -d -m 0755 -o root -g root /usr/local/src/patches
# Hand the directory over to the current user.
sudo chown "$USER":"$(id -gn)" /usr/local/src/patches
# Open the patch file with the configured editor.
${EDITOR:-nano} /usr/local/src/patches/ossfs-ubuntu2604-compat.patch

Full contents:

/usr/local/src/patches/ossfs-ubuntu2604-compat.patch
# /usr/local/src/patches/ossfs-ubuntu2604-compat.patch
diff --git a/CMakeLists.txt b/CMakeLists.txt
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -168,6 +168,14 @@
add_library(ossfs2_common STATIC ${ossfs2_common_srcs})
+# PhotonLibOS and some ossfs2 sources rely on transitive inclusion of
+# standard headers. Preserve each "-include <header>" option as an
+# indivisible shell group so CMake's option de-duplication does not
+# separate the option from its argument.
+target_compile_options(ossfs2_common PRIVATE
+ "$<$<COMPILE_LANGUAGE:CXX>:SHELL:-include cstdint>"
+ "$<$<COMPILE_LANGUAGE:CXX>:SHELL:-include algorithm>")
+
target_sources(ossfs2_common PRIVATE
${PHOTON_PATCHED_SRC_DIR}/ecosystem/oss_patched.cpp
)
@@ -182,6 +190,11 @@
src/*.cpp)
add_executable(ossfs2 ${ossfs2_srcs})
+
+# The executable sources also include PhotonLibOS public headers directly.
+target_compile_options(ossfs2 PRIVATE
+ "$<$<COMPILE_LANGUAGE:CXX>:SHELL:-include cstdint>"
+ "$<$<COMPILE_LANGUAGE:CXX>:SHELL:-include algorithm>")
# we will install libfuse to /usr/local/lib64/ossfs2, make sure ossfs2
# can find it instead of using the system one

target_compile_options applies options to the given target; PRIVATE prevents them from propagating to its consumers, and $<COMPILE_LANGUAGE:CXX> restricts them to C++. CMake’s SHELL: prefix keeps each -include grouped with its header during de-duplication; it does not select or run a command interpreter.

5. Verify and apply the patch with patch

Terminal window
# Enter the original tree.
cd /usr/local/src/ossfs
# Check that the tree has no local changes.
git status --short
# Simulate the application without modifying files.
patch --dry-run -p1 \
< /usr/local/src/patches/ossfs-ubuntu2604-compat.patch
# Apply the actual patch.
patch -p1 \
< /usr/local/src/patches/ossfs-ubuntu2604-compat.patch
# Show the change that was made.
git diff -- CMakeLists.txt

To revert it:

Terminal window
# Reverse the applied patch.
patch -R -p1 \
< /usr/local/src/patches/ossfs-ubuntu2604-compat.patch

6. Configure CMake 4

CMake 4 removed compatibility with policies older than CMake 3.5. The gflags 2.2.2 dependency declares an old version. The CMAKE_POLICY_VERSION_MINIMUM=3.5 variable makes it possible to configure the subproject without modifying its source.

Terminal window
# Enter the repository.
cd /usr/local/src/ossfs
# Remove any previous binary tree.
rm -rf build
# Export the policy minimum for CMake and external subprocesses.
export CMAKE_POLICY_VERSION_MINIMUM=3.5
# Configure sources, output, Release type and DEB generator.
cmake \
-S . \
-B build \
-DCMAKE_BUILD_TYPE=Release \
-DCPACK_GENERATOR=DEB \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON

7. Build

Terminal window
# Reassert the variable for the current session.
export CMAKE_POLICY_VERSION_MINIMUM=3.5
# Build using every available logical processor.
cmake --build build --parallel "$(nproc)"

Check the binary:

Terminal window
# Identify the format and architecture of the executable.
file build/ossfs2
# Show the version without installing it.
build/ossfs2 --version

8. Generate a DEB package with CPack

I normally create installation packages with checkinstall, but the project already contains install rules and CPack configuration. That makes CPack more reproducible than checkinstall here.

Terminal window
# Enter the build tree.
cd /usr/local/src/ossfs/build
# Check that the Debian variables ended up in CPackConfig.cmake.
grep -E \
'CPACK_GENERATOR|CPACK_DEBIAN_PACKAGE_MAINTAINER|CPACK_DEBIAN_PACKAGE_DEPENDS' \
CPackConfig.cmake
# Generate only the main component in DEB format.
cpack \
-G DEB \
-C Release \
-D CPACK_COMPONENTS_ALL=main

Locate and inspect it:

Terminal window
# Save the path of the first main package found.
PACKAGE_PATH="$(find . -maxdepth 1 -type f -name 'ossfs2_*.deb' | head -n1)"
# Show the package metadata.
dpkg-deb --info "$PACKAGE_PATH"
# Show the files it will install.
dpkg-deb --contents "$PACKAGE_PATH"
# Show specific fields from the Debian control file.
dpkg-deb -f "$PACKAGE_PATH" \
Package Version Architecture Maintainer Depends

9. Install the package

Terminal window
# Install the local file and let APT resolve dependencies.
sudo apt install "$PACKAGE_PATH"
# Locate the installed executable.
command -v ossfs2
# Check the installed version.
ossfs2 --version
# Query the package status in dpkg.
dpkg-query -W \
-f='${Package}\t${Version}\t${Status}\n' \
ossfs2

10. systemd design for several buckets

A template unit, two shared scripts, an optional credentials file and one file per instance will be used:

flowchart LR
    U["ossfs2@.service"] --> C["/etc/ossfs2/credentials.env"]
    U --> I["/etc/ossfs2/%i.env"]
    U --> M["ossfs2-systemd-mount"]
    U --> X["ossfs2-systemd-umount"]
    I --> D["documents"]
    I --> MM["multimedia"]
    I --> R["backups"]

%i is replaced with the name after @. ossfs2@documents.service will read /etc/ossfs2/documents.env.

11. Create directories

Terminal window
# Create the restricted configuration directory.
sudo install -d -m 0750 -o root -g root /etc/ossfs2
# Create the log root directory.
sudo install -d -m 0755 -o root -g root /var/log/ossfs2
# Create the root directory for mount points.
sudo install -d -m 0755 -o root -g root /mnt/oss

12. Store credentials

Create a restricted file:

Terminal window
# Create an empty file owned by root and readable only by root.
sudo install -m 0600 -o root -g root \
/dev/null /etc/ossfs2/credentials.env
# Edit the file administratively.
sudoedit /etc/ossfs2/credentials.env

Contents:

/etc/ossfs2/credentials.env
# Public identifier of the AccessKey pair.
OSS_ACCESS_KEY_ID=LTAI_REPLACE
# Private secret corresponding to the identifier above.
OSS_ACCESS_KEY_SECRET=REPLACE_WITH_SECRET

Lines starting with # are comments. Do not add spaces around =, and do not publish this file.

13. Shared mount script

Terminal window
# Create the empty script with executable permissions.
sudo install -m 0755 -o root -g root \
/dev/null /usr/local/sbin/ossfs2-systemd-mount
# Edit the script.
sudoedit /usr/local/sbin/ossfs2-systemd-mount

/usr/local/sbin/ossfs2-systemd-mount has the following contents:

/usr/local/sbin/ossfs2-systemd-mount
#!/usr/bin/env bash
# Selects Bash through PATH.
set -euo pipefail
# Exits on errors, unset variables or pipeline failures.
OSSFS_BINARY=/usr/local/bin/ossfs2
# Defines the absolute path to the installed executable.
required_variables=(
# Starts the list of mandatory variables.
OSSFS_BUCKET
# Real bucket name.
OSSFS_ENDPOINT
# Regional OSS endpoint.
OSSFS_MOUNTPOINT
# Local mount directory.
OSSFS_LOG_DIR
# Dedicated log directory.
OSSFS_FILE_MODE
# Octal permissions for files in the bucket.
OSSFS_DIR_MODE
# Octal permissions for directories in the bucket.
)
# Ends the list.
for variable in "${required_variables[@]}"; do
# Iterates over the mandatory names.
if [[ -z "${!variable:-}" ]]; then
# Indirectly checks whether each variable is empty.
printf 'Missing mandatory variable: %s\n' "$variable" >&2
# Writes the error to stderr.
exit 1
# Exits reporting failure.
fi
# Ends the check.
done
# Ends the loop.
for variable in OSSFS_FILE_MODE OSSFS_DIR_MODE; do
# Iterates over the variables that contain permissions.
if [[ ! "${!variable}" =~ ^0[0-7]{3}$ ]]; then
# Requires four octal digits, for example 0644 or 0750.
printf '%s must be a four-digit octal mode (for example, 0644): %s\n' \
"$variable" "${!variable}" >&2
# Explains the expected format and shows the invalid value.
exit 1
# Prevents mounting with ambiguous or invalid permissions.
fi
# Ends validation.
done
# Ends the mode loop.
if [[ ! -x "$OSSFS_BINARY" ]]; then
# Checks that the executable exists.
printf 'Invalid executable: %s\n' "$OSSFS_BINARY" >&2
# Reports the offending path.
exit 1
# Exits with failure.
fi
# Ends the check.
install -d -m 0755 -o root -g root "$OSSFS_MOUNTPOINT"
# Creates the mount point if it does not exist.
install -d -m 0755 -o root -g root "$OSSFS_LOG_DIR"
# Creates the instance log directory.
if mountpoint -q "$OSSFS_MOUNTPOINT"; then
# Silently checks whether it is already mounted.
printf '%s is already mounted.\n' "$OSSFS_MOUNTPOINT"
# Records that there is nothing to do.
exit 0
# Exits successfully.
fi
# Ends the check.
args=(
# Starts the argument array.
mount
# Selects the mount subcommand.
"$OSSFS_MOUNTPOINT"
# Indicates the mount point.
"--oss_endpoint=$OSSFS_ENDPOINT"
# Indicates the OSS endpoint.
"--oss_bucket=$OSSFS_BUCKET"
# Indicates the bucket.
"--log_dir=$OSSFS_LOG_DIR"
# Keeps logs separated per instance.
"--file_mode=$OSSFS_FILE_MODE"
# Sets the permissions OSSFS2 reports for all files.
"--dir_mode=$OSSFS_DIR_MODE"
# Sets the permissions OSSFS2 reports for all directories.
)
# Ends the array.
if [[ -n "${OSSFS_RAM_ROLE:-}" ]]; then
# Checks whether a RAM Role was defined.
args+=("--ram_role=$OSSFS_RAM_ROLE")
# Adds the role to the command.
fi
# Ends the RAM Role option.
exec "$OSSFS_BINARY" "${args[@]}"
# Replaces the shell with OSSFS2 and preserves its exit code.

Validate it:

Terminal window
# Parse the syntax without running the script.
sudo bash -n /usr/local/sbin/ossfs2-systemd-mount

14. Shared unmount script

Terminal window
# Create the executable script.
sudo install -m 0755 -o root -g root \
/dev/null /usr/local/sbin/ossfs2-systemd-umount
# Edit the script.
sudoedit /usr/local/sbin/ossfs2-systemd-umount

Contents:

/usr/local/sbin/ossfs2-systemd-umount
#!/usr/bin/env bash
# Enables strict mode.
set -euo pipefail
# Checks that the mount point variable exists.
if [[ -z "${OSSFS_MOUNTPOINT:-}" ]]; then
# Reports the error on stderr.
echo 'OSSFS_MOUNTPOINT is not defined.' >&2
# Exits with failure.
exit 1
# Ends the validation.
fi
# Checks whether it is already unmounted.
if ! mountpoint -q "$OSSFS_MOUNTPOINT"; then
# Reports that there is nothing to do.
printf '%s is not mounted.\n' "$OSSFS_MOUNTPOINT"
# Exits successfully.
exit 0
# Ends the check.
fi
# Unmounts the filesystem.
umount "$OSSFS_MOUNTPOINT"

15. Template unit

Terminal window
# Create or edit the template unit.
sudoedit /etc/systemd/system/ossfs2@.service

Write the contents:

/etc/systemd/system/ossfs2@.service
# Starts metadata and ordering.
[Unit]
# %i is replaced with the instance name.
Description=Alibaba Cloud OSS mount through OSSFS2 for %i
# Records the main documentation.
Documentation=https://github.com/aliyun/ossfs
# Asks systemd to try to reach an operational network.
Wants=network-online.target
# Orders the mount after network-online.target.
After=network-online.target
# Starts the service definition.
[Service]
# ExecStart performs a finite operation.
Type=oneshot
# Loads credentials; the - prefix makes the file optional.
EnvironmentFile=-/etc/ossfs2/credentials.env
# Loads the mandatory instance configuration.
EnvironmentFile=/etc/ossfs2/%i.env
# Runs the shared mount script.
ExecStart=/usr/local/sbin/ossfs2-systemd-mount
# Runs the unmount when the unit is stopped.
ExecStop=/usr/local/sbin/ossfs2-systemd-umount
# Keeps the unit active after ExecStart finishes.
RemainAfterExit=yes
# Limits startup to 120 seconds.
TimeoutStartSec=120
# Limits unmounting to 60 seconds.
TimeoutStopSec=60
# Sends stdout to the journal.
StandardOutput=journal
# Sends stderr to the journal.
StandardError=journal
# Defines how the unit is enabled.
[Install]
# Ties it to the multi-user boot target.
WantedBy=multi-user.target

Wants= creates a weak dependency. After= defines ordering. Type=oneshot and RemainAfterExit=yes make it possible to represent a persistent mount created by a finite command. The - prefix in EnvironmentFile allows credentials to be omitted when a RAM Role is used.

Validate and reload:

Terminal window
# Validate the unit.
sudo systemd-analyze verify \
/etc/systemd/system/ossfs2@.service
# Reload the service manager configuration.
sudo systemctl daemon-reload

16. Per-bucket file

Example, documents:

Terminal window
# Create a restricted file for the instance.
sudo install -m 0600 -o root -g root \
/dev/null /etc/ossfs2/documents.env
# Edit its variables.
sudoedit /etc/ossfs2/documents.env

Contents:

/etc/ossfs2/documents.env
# Real bucket name.
OSSFS_BUCKET=example-documents-bucket
# Regional endpoint; use -internal only with internal connectivity.
OSSFS_ENDPOINT=oss-cn-region-internal.aliyuncs.com
# Dedicated local mount point.
OSSFS_MOUNTPOINT=/mnt/oss/documents
# Dedicated log directory.
OSSFS_LOG_DIR=/var/log/ossfs2/documents
# Permissions for all files visible in the mount.
OSSFS_FILE_MODE=0640
# Permissions for all directories visible in the mount.
OSSFS_DIR_MODE=0750
# Uncomment to use a RAM Role instead of AccessKeys.
# OSSFS_RAM_ROLE=example-oss-role

Repeat the pattern with /etc/ossfs2/multimedia.env, /etc/ossfs2/backups.env or other names. Each instance must have its own mount point and log directory. Set OSSFS_FILE_MODE and OSSFS_DIR_MODE for the access required by each bucket; the values apply globally to the files and directories presented by OSSFS2, including newly created ones.

17. Test and enable mounts

Terminal window
# Start one instance without enabling it yet.
sudo systemctl start ossfs2@documents.service
# Show its status.
systemctl status ossfs2@documents.service
# Show the logs of the current boot.
sudo journalctl -u ossfs2@documents.service \
-b --no-pager
# Check the mount.
findmnt /mnt/oss/documents
# List the contents.
ls -la /mnt/oss/documents

Test writing and reading:

Terminal window
# Create a test object.
printf 'OSSFS2 test with systemd.\n' \
| sudo tee /mnt/oss/documents/test.txt >/dev/null
# Read the object.
sudo cat /mnt/oss/documents/test.txt
# Delete the object.
sudo rm /mnt/oss/documents/test.txt

Enable several instances:

Terminal window
# Enable and start three independent mounts.
sudo systemctl enable --now \
ossfs2@documents.service \
ossfs2@multimedia.service \
ossfs2@backups.service
# List all loaded instances.
systemctl list-units 'ossfs2@*.service' --all
# Show every submount under /mnt/oss.
findmnt --submounts /mnt/oss

18. Day-to-day operation

Terminal window
# Stop and unmount a single instance.
sudo systemctl stop ossfs2@multimedia.service
# Restart an instance.
sudo systemctl restart ossfs2@multimedia.service
# Disable and stop an instance.
sudo systemctl disable --now ossfs2@multimedia.service
# Follow the logs in real time.
sudo journalctl -u ossfs2@documents.service -f

19. Diagnostics

If the build fails on gflags:

Terminal window
# Check the CMake compatibility variable.
printf '%s\n' "${CMAKE_POLICY_VERSION_MINIMUM:-not defined}"

If uint64_t does not name a type appears:

Terminal window
# Check the options added by the patch.
grep -n 'SHELL:-include' /usr/local/src/ossfs/CMakeLists.txt

If CPack cannot find the maintainer:

Terminal window
# Reconfigure, explicitly enabling the DEB generator.
cmake -S /usr/local/src/ossfs \
-B /usr/local/src/ossfs/build \
-DCMAKE_BUILD_TYPE=Release \
-DCPACK_GENERATOR=DEB

If unmounting reports that the target is busy:

Terminal window
# Show processes using the mount.
sudo fuser -vm /mnt/oss/documents

20. Security and limitations

Appendix A. Verified official references

  1. Official OSSFS2 repository: https://github.com/aliyun/ossfs
  2. OSSFS2 mount options: https://help.aliyun.com/en/oss/developer-reference/description-of-mount-options
  3. OSSFS2 automatic mounting: https://help.aliyun.com/en/oss/developer-reference/configure-auto-mount-on-for-ossfs-2-0
  4. CPack DEB generator: https://cmake.org/cmake/help/latest/cpack_gen/deb.html
  5. CMAKE_POLICY_VERSION_MINIMUM: https://cmake.org/cmake/help/latest/variable/CMAKE_POLICY_VERSION_MINIMUM.html
  6. target_compile_options and SHELL: groups: https://cmake.org/cmake/help/latest/command/target_compile_options.html
  7. systemd service units: https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html
  8. Templates, instances and %i: https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html
  9. EnvironmentFile and execution environment: https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html
  10. network-online.target: https://www.freedesktop.org/software/systemd/man/latest/systemd.special.html
  11. Management with systemctl: https://www.freedesktop.org/software/systemd/man/latest/systemctl.html
  12. Querying logs with journalctl: https://www.freedesktop.org/software/systemd/man/latest/journalctl.html

Appendix B. Summarized build sequence

Terminal window
# Enter the original tree.
cd /usr/local/src/ossfs
# Simulate the patch.
patch --dry-run -p1 \
< /usr/local/src/patches/ossfs-ubuntu2604-compat.patch
# Apply the patch.
patch -p1 \
< /usr/local/src/patches/ossfs-ubuntu2604-compat.patch
# Remove previous builds.
rm -rf build
# Export the compatibility required by CMake 4.
export CMAKE_POLICY_VERSION_MINIMUM=3.5
# Configure the build and Debian packaging.
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Release \
-DCPACK_GENERATOR=DEB
# Build in parallel.
cmake --build build --parallel "$(nproc)"
# Enter the binary tree.
cd build
# Generate the main package.
cpack -G DEB -C Release \
-D CPACK_COMPONENTS_ALL=main


Previous Post
p5.js: art with JavaScript
Next Post
Machines: Jean-Michel Jarre's new book.