Linux: determining app: and app-storage: directories

Follow-up. In my file system API:

  • The app: URL resolves to a file (an "asset") in the app's installation directory.
  • The app-storage: URL resolves to a file/directory in the app's data/settings directory. (E.g. store databases, cookies, ... pretty much compatible with the web Origin Private File System)

I had a ready implementation, but didn't support Linux AppImage, FlatPak and ?Steam?.

I've put a FIXME in this code for what needs to be changed to a something else: (e.g. PathBuf::from(&std::env::var("HOME").unwrap()).join(".local/share"))

I determined if the app is running in Flatpak/AppImage/Snap according to this.

use std::path::PathBuf;
use cfg_if::cfg_if;

/// System-wide app: directory.
pub fn system_wide_app_dir() -> PathBuf {
    cfg_if! {
        if #[cfg(target_os = "windows")] {
            // Windows
            std::env::current_exe().unwrap().parent().unwrap().to_owned()
        } else if #[cfg(target_os = "macos")] {
            // Mac
            std::env::current_exe().unwrap().parent().unwrap().to_owned()
        } else {
            // Linux
            if std::env::var("container").is_ok() {
                // FlatPak (system-wide installation)
                PathBuf::from(FIXME).join(unsafe { crate::application::bootstrap::APPLICATION_ID.unwrap() })
            } else if std::env::var("APPIMAGE").is_ok() {
                // AppImage
                std::env::current_exe().unwrap().parent().unwrap().to_owned()
            } else if std::env::var("SNAP").is_ok() {
                // Snap
                PathBuf::from(FIXME).join(unsafe { crate::application::bootstrap::APPLICATION_ID.unwrap() })
            } else {
                // System-wide installation through scratch-made installer
                PathBuf::from("/usr/local/share").join(unsafe { crate::application::bootstrap::APPLICATION_ID.unwrap() })
            }
        }
    }
}

/// User app: directory.
pub fn user_app_dir() -> PathBuf {
    cfg_if! {
        if #[cfg(target_os = "windows")] {
            // Windows
            std::env::current_exe().unwrap().parent().unwrap().to_owned()
        } else if #[cfg(target_os = "macos")] {
            // Mac
            std::env::current_exe().unwrap().parent().unwrap().to_owned()
        } else {
            // Linux
            if std::env::var("container").is_ok() {
                // FlatPak (system-wide installation)
                PathBuf::from(FIXME).join(unsafe { crate::application::bootstrap::APPLICATION_ID.unwrap() })
            } else if std::env::var("APPIMAGE").is_ok() {
                // AppImage
                std::env::current_exe().unwrap().parent().unwrap().to_owned()
            } else if std::env::var("SNAP").is_ok() {
                // Snap
                PathBuf::from(FIXME).join(unsafe { crate::application::bootstrap::APPLICATION_ID.unwrap() })
            } else {
                // User installation through scratch-made installer
                dirs::data_dir().unwrap().join(unsafe { crate::application::bootstrap::APPLICATION_ID.unwrap() })
            }
        }
    }
}

/// General app-storage: directory (Windows, Linux, Mac)
pub fn general_app_storage_dir() -> PathBuf {
    cfg_if! {
        if #[cfg(target_os = "linux")] {
            // Linux
            if std::env::var("container").is_ok() {
                // FlatPak
                PathBuf::from(FIXME).join(unsafe { crate::application::bootstrap::APPLICATION_ID.unwrap() })
            } else if std::env::var("APPIMAGE").is_ok() {
                // AppImage
                dirs::config_dir().unwrap().join(unsafe { crate::application::bootstrap::APPLICATION_ID.unwrap() })
            } else if std::env::var("SNAP").is_ok() {
                // Snap
                PathBuf::from(FIXME).join(unsafe { crate::application::bootstrap::APPLICATION_ID.unwrap() })
            } else {
                // app-storage: directory after scratch-made installer
                dirs::config_dir().unwrap().join(unsafe { crate::application::bootstrap::APPLICATION_ID.unwrap() })
            }
        } else {
            // (Windows, Mac)
            dirs::config_dir().unwrap().join(unsafe { crate::application::bootstrap::APPLICATION_ID.unwrap() })
        }
    }
}

Basically I just need to know:

  • The system-wide app: in...
    • FlatPak
    • ?Snap?
  • The user app: directory in...
    • FlatPak
    • ?Snap?
  • The app-storage: directory (always user-specific) in
    • FlatPak
    • ?Snap?

I'm not sure I need Snap

That's what AI says:

Let’s carefully map Flatpak and Snap to your three virtual URL schemes (app:, app-storage:), split by system-wide vs user-specific.


:small_blue_diamond: Flatpak

Flatpak is OSTree-based, so the app’s files are read-only in the installation tree, while writable storage always goes in ~/.var/app/<APP_ID>/.

  • System-wide app:
    /var/lib/flatpak/app/<APP_ID>/current/active/files/
    (this is the mounted installation tree for the Flatpak, containing the bundled assets and binaries)

  • User app: (when installed with --user)
    ~/.local/share/flatpak/app/<APP_ID>/current/active/files/

  • app-storage: (user-specific, writable sandbox storage)
    ~/.var/app/<APP_ID>/
    This directory exists regardless of whether the Flatpak was installed system-wide or per-user.
    It contains config/, data/, cache/ — similar to XDG dirs, but private to the Flatpak.


:small_blue_diamond: Snap

Snap apps are mounted into /snap/<APP_NAME>/<REVISION>/ as read-only squashfs images. User data lives in ~/snap/<APP_NAME>/.

  • System-wide app:
    /snap/<APP_NAME>/current/
    (current is a symlink to the latest revision)

  • User app:
    → Same as system-wide: /snap/<APP_NAME>/current/
    (Snaps are always installed system-wide; there’s no user-only install)

  • app-storage: (user-specific)
    ~/snap/<APP_NAME>/current/
    (symlink into the revisioned ~/snap/<APP_NAME>/<REVISION>/)

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.