Compare commits
	
		
			84 Commits
		
	
	
		
			v2.0.4-rc
			...
			144baf4371
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 144baf4371 | |||
| c6ade2e5f7 | |||
| fb89cc08ae | |||
| f08870b03c | |||
| 711e3f73cf | |||
| ebb620fdb2 | |||
| fbf9c85d48 | |||
| d5e0252ed3 | |||
| 06b79ffd2d | |||
| 690902b31d | |||
| 0bfe1e1ccd | |||
| a8723a6b02 | |||
| 1d88d26d0a | |||
| 757c880616 | |||
| d65bd6af35 | |||
| b285478cc5 | |||
| 31cd5acaee | |||
| 65036f2957 | |||
| 2362300bba | |||
| f61f3d5fa4 | |||
| a2b8998f4a | |||
| a4d53c1011 | |||
| 4b925c15c2 | |||
| bbd82e3f0f | |||
| 37f2cbc78d | |||
| 1ad3704fa0 | |||
| a6e70d93cb | |||
| 967324a368 | |||
| 69b31bfde8 | |||
| 8b4724a9c1 | |||
| 6a0d50bc66 | |||
| 69910bef4c | |||
| 8c298c84c5 | |||
| 47dea2cc38 | |||
| d8b476e80a | |||
| c86c6e2ec6 | |||
| c397497eb7 | |||
| f8803dfbf0 | |||
| 5d5cacc482 | |||
| 3ce4210d56 | |||
| d109344544 | |||
| ff746a7bec | |||
| a613ec77ff | |||
| 00d3dd95a8 | |||
| 28d1789f04 | |||
| 0603463885 | |||
| 88398485e1 | |||
| 908e75c696 | |||
| dab8c61f87 | |||
| e0cf58b01e | |||
| 281d3758e0 | |||
| dfa170022a | |||
| 52c2780283 | |||
| c2dbfc970a | |||
| 6f9b1f8f08 | |||
| 078d603be9 | |||
| 983e47103b | |||
| 8d2024d34b | |||
| 4f2ee2ad99 | |||
| 533938bcef | |||
| 98edf33be4 | |||
| a080c9ff86 | |||
| e6cdcd74a1 | |||
| 573ae549be | |||
| fca149f998 | |||
| 76e375c488 | |||
| 3f9322f659 | |||
| c286d496c3 | |||
| 56ba0fcb83 | |||
| dcafb104ea | |||
| ab757dfd36 | |||
| eec2d2e9a9 | |||
| 8c1c91e02b | |||
| bb85015733 | |||
| 883de836c6 | |||
| bf2bdd1b5d | |||
| f1e82d8f9f | |||
| f5c4aebdac | |||
| f2f9e8fd15 | |||
| 2a673915af | |||
| df3db38ae7 | |||
| b0b69c6dd4 | |||
| 11b118a30f | |||
| 62555e6125 | 
@@ -3,6 +3,7 @@ _mkgmtime
 | 
			
		||||
_sh_denyno
 | 
			
		||||
_sh_denyrd
 | 
			
		||||
_sh_denyrw
 | 
			
		||||
_spawnv
 | 
			
		||||
aarch64
 | 
			
		||||
advapi32
 | 
			
		||||
armv8
 | 
			
		||||
@@ -114,6 +115,7 @@ googletest
 | 
			
		||||
gpath
 | 
			
		||||
gtest_version
 | 
			
		||||
has_setxattr
 | 
			
		||||
hkey
 | 
			
		||||
httpapi
 | 
			
		||||
httplib
 | 
			
		||||
icudata
 | 
			
		||||
@@ -121,6 +123,7 @@ icui18n
 | 
			
		||||
icuuc
 | 
			
		||||
iostreams
 | 
			
		||||
iphlpapi
 | 
			
		||||
ipstream
 | 
			
		||||
jthread
 | 
			
		||||
libbitcoin
 | 
			
		||||
libbitcoinsystem
 | 
			
		||||
@@ -142,6 +145,7 @@ libuuid_include_dirs
 | 
			
		||||
libvlc
 | 
			
		||||
linkflags
 | 
			
		||||
localappdata
 | 
			
		||||
lpbyte
 | 
			
		||||
lptr
 | 
			
		||||
lpwstr
 | 
			
		||||
markdownlint
 | 
			
		||||
@@ -154,8 +158,10 @@ mtune
 | 
			
		||||
musl-libc
 | 
			
		||||
nana
 | 
			
		||||
ncrypt
 | 
			
		||||
nlohmann
 | 
			
		||||
nlohmann_json
 | 
			
		||||
nmakeprg
 | 
			
		||||
nohup
 | 
			
		||||
nominmax
 | 
			
		||||
ntstatus
 | 
			
		||||
nullptr
 | 
			
		||||
@@ -163,6 +169,7 @@ nuspell_version
 | 
			
		||||
oleaut32
 | 
			
		||||
openal_version
 | 
			
		||||
openssldir
 | 
			
		||||
pistream
 | 
			
		||||
pkgconfig
 | 
			
		||||
plarge_integer
 | 
			
		||||
plex
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
*.tgz filter=lfs diff=lfs merge=lfs -text
 | 
			
		||||
*.tar.gz filter=lfs diff=lfs merge=lfs -text
 | 
			
		||||
*.tar.xz filter=lfs diff=lfs merge=lfs -text
 | 
			
		||||
*.zip filter=lfs diff=lfs merge=lfs -text
 | 
			
		||||
@@ -1,36 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
    "configurations": {
 | 
			
		||||
        "UnixDebug": {
 | 
			
		||||
            "adapter": "vscode-cpptools",
 | 
			
		||||
            "configuration": {
 | 
			
		||||
                "request": "launch",
 | 
			
		||||
                "program": "${workspaceRoot}/build/debug/repertory",
 | 
			
		||||
                "args": ["-f", "/home/sgraves/mnt"],
 | 
			
		||||
                "cwd": "${workspaceRoot}/build/debug",
 | 
			
		||||
                "environment": [],
 | 
			
		||||
                "externalConsole": true,
 | 
			
		||||
                "MIMode": "gdb",
 | 
			
		||||
                "stopAtEntry": true,
 | 
			
		||||
                "logging": {
 | 
			
		||||
                    "engineLogging": false
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "UnixDebugTest": {
 | 
			
		||||
            "adapter": "vscode-cpptools",
 | 
			
		||||
            "configuration": {
 | 
			
		||||
                "request": "launch",
 | 
			
		||||
                "program": "${workspaceRoot}/build/debug/unittests",
 | 
			
		||||
                "args": ["--gtest_filter=file_manager.can_close_after_download_timeout"],
 | 
			
		||||
                "cwd": "${workspaceRoot}/build",
 | 
			
		||||
                "environment": [],
 | 
			
		||||
                "externalConsole": true,
 | 
			
		||||
                "MIMode": "gdb",
 | 
			
		||||
                "stopAtEntry": true,
 | 
			
		||||
                "logging": {
 | 
			
		||||
                    "engineLogging": false
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								CHANGELOG.md
									
									
									
									
									
								
							@@ -1,11 +1,49 @@
 | 
			
		||||
# Changelog
 | 
			
		||||
 | 
			
		||||
## v2.0.6-release
 | 
			
		||||
 | 
			
		||||
### Issues
 | 
			
		||||
 | 
			
		||||
* ~~\#12 [Unit Test] Complete all providers unit tests~~
 | 
			
		||||
* ~~\#21 [Unit Test] Complete WinFSP unit tests~~
 | 
			
		||||
* ~~\#22 [Unit Test] Complete FUSE unit tests~~
 | 
			
		||||
* ~~\#33 Complete initial v2.0 documentation~~
 | 
			
		||||
* \#42 [bug] Remote mount directory listing on Windows connected to Linux is failing
 | 
			
		||||
* \#43 [bug] Directories are not importing properly for Sia
 | 
			
		||||
* \#44 [bug] Windows-to-Linux remote mount ignores `CREATE_NEW`
 | 
			
		||||
* \#45 [bug] Windows-to-Linux remote mount is not handling attempts to remove a non-empty directory properly
 | 
			
		||||
* \#46 [bug] Changes to maximum cache size should be updated live
 | 
			
		||||
* \#47 [bug] Windows-to-Linux remote mount is allowing directory rename when directory is not empty
 | 
			
		||||
 | 
			
		||||
### Changes from v2.0.5-rc
 | 
			
		||||
 | 
			
		||||
* Drive letters in UI should always be lowercase
 | 
			
		||||
* Migrated to v2 error handling
 | 
			
		||||
 | 
			
		||||
## v2.0.5-rc
 | 
			
		||||
 | 
			
		||||
<!-- markdownlint-disable-next-line -->
 | 
			
		||||
### Issues
 | 
			
		||||
 | 
			
		||||
* \#39 Create management portal in Flutter
 | 
			
		||||
 | 
			
		||||
### Changes from v2.0.4-rc
 | 
			
		||||
 | 
			
		||||
* Continue documentation updates
 | 
			
		||||
* Fixed `-status` command erasing active mount information
 | 
			
		||||
* Fixed overlapping HTTP REST API port's
 | 
			
		||||
* Refactored/fixed instance locking
 | 
			
		||||
* Removed passwords and secret key values from API calls
 | 
			
		||||
* Renamed setting `ApiAuth` to `ApiPassword`
 | 
			
		||||
* Require `--name,-na` option for encryption provider
 | 
			
		||||
 | 
			
		||||
## v2.0.4-rc
 | 
			
		||||
 | 
			
		||||
### BREAKING CHANGES
 | 
			
		||||
 | 
			
		||||
* `renterd` v2.0.0+ is now required. Prior versions will fail to mount.
 | 
			
		||||
 | 
			
		||||
<!-- markdownlint-disable-next-line -->
 | 
			
		||||
### Issues
 | 
			
		||||
 | 
			
		||||
* \#35 [bug] Low frequency check is set to '0' instead of 1 hour by default
 | 
			
		||||
@@ -52,6 +90,7 @@
 | 
			
		||||
 | 
			
		||||
## v2.0.2-rc
 | 
			
		||||
 | 
			
		||||
<!-- markdownlint-disable-next-line -->
 | 
			
		||||
### BREAKING CHANGES
 | 
			
		||||
 | 
			
		||||
* Refactored `config.json` - will need to verify configuration settings prior to mounting
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										358
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										358
									
								
								README.md
									
									
									
									
									
								
							@@ -3,11 +3,42 @@
 | 
			
		||||
Repertory allows you to mount S3 and Sia via FUSE on Linux or via WinFSP
 | 
			
		||||
on Windows.
 | 
			
		||||
 | 
			
		||||
## Contents
 | 
			
		||||
 | 
			
		||||
1. [Details and Features](#details-and-features)
 | 
			
		||||
2. [Minimum Requirements](#minimum-requirements)
 | 
			
		||||
   1. [Supported Operating Systems](#supported-operating-systems)
 | 
			
		||||
3. [GUI](#gui)
 | 
			
		||||
4. [Usage](#usage)
 | 
			
		||||
   1. [Important Options](#important-options)
 | 
			
		||||
   2. [Sia](#sia)
 | 
			
		||||
      * [Sia Initial Configuration](#sia-initial-configuration)
 | 
			
		||||
      * [Sia Mounting](#sia-mounting)
 | 
			
		||||
      * [Sia Configuration File](#sia-configuration-file)
 | 
			
		||||
   3. [S3](#s3)
 | 
			
		||||
      * [S3 Initial Configuration](#s3-initial-configuration)
 | 
			
		||||
      * [S3 Mounting](#s3-mounting)
 | 
			
		||||
      * [S3 Configuration File](#s3-configuration-file)
 | 
			
		||||
5. [Data Directories](#data-directories)
 | 
			
		||||
   1. [Linux Directories](#linux-directories)
 | 
			
		||||
   2. [Windows Directories](#windows-directories)
 | 
			
		||||
6. [Remote Mounting](#remote-mounting)
 | 
			
		||||
   1. [Server Setup](#server-setup)
 | 
			
		||||
      * [Remote Mount Configuration File Section](#remote-mount-configuration-file-section)
 | 
			
		||||
   2. [Client Setup](#client-setup)
 | 
			
		||||
      * [Client Remote Mounting](#client-remote-mounting)
 | 
			
		||||
      * [Remote Mount Configuration File](#remote-mount-configuration-file)
 | 
			
		||||
7. [Compiling](#compiling)
 | 
			
		||||
   1. [Linux Compilation](#linux-compilation)
 | 
			
		||||
   2. [Windows Setup](#windows-compilation)
 | 
			
		||||
8. [Credits](#credits)
 | 
			
		||||
9. [Developer Public Key](#developer-public-key)
 | 
			
		||||
10. [Consult the Wiki for additional information](https://git.fifthgrid.com/BlockStorage/repertory/wiki)
 | 
			
		||||
 | 
			
		||||
## Details and Features
 | 
			
		||||
 | 
			
		||||
* Optimized for [Plex Media Server](https://www.plex.tv/)
 | 
			
		||||
* Single application to mount S3 and/or Sia
 | 
			
		||||
* Remote mounting of Repertory instances on Linux and Windows
 | 
			
		||||
* Remote mounting of `repertory` instances on Linux and Windows
 | 
			
		||||
  * Securely share your mounts over TCP/IP via `XChaCha20-Poly1305` with other systems on your network or over the internet.
 | 
			
		||||
* Cross-platform support (Linux 64-bit, Linux arm64/aarch64, Windows 64-bit)
 | 
			
		||||
* Optionally encrypt file names and file data via `XChaCha20-Poly1305` in S3 mounts
 | 
			
		||||
@@ -15,52 +46,159 @@ on Windows.
 | 
			
		||||
## Minimum Requirements
 | 
			
		||||
 | 
			
		||||
* [Sia renterd](https://github.com/SiaFoundation/renterd/releases) v2.0.0+ for Sia support
 | 
			
		||||
* Only 64-bit operating systems are supported
 | 
			
		||||
  * By default, Linux requires `fusermount3`; otherwise, `repertory` must be manually compiled with `libfuse2` support
 | 
			
		||||
* Linux requires `fusermount3`; otherwise, `repertory` must be manually compiled with `libfuse2` support
 | 
			
		||||
* Windows requires the following dependencies to be installed:
 | 
			
		||||
  * [WinFSP 2023](https://github.com/winfsp/winfsp/releases/download/v2.0/winfsp-2.0.23075.msi)
 | 
			
		||||
 | 
			
		||||
## Supported Operating Systems
 | 
			
		||||
### Supported Operating Systems
 | 
			
		||||
 | 
			
		||||
Only 64-bit operating systems are supported
 | 
			
		||||
 | 
			
		||||
* Linux `arm64/aarch64`
 | 
			
		||||
* Linux `amd64`
 | 
			
		||||
* Windows 64-bit 10, 11
 | 
			
		||||
 | 
			
		||||
## GUI
 | 
			
		||||
 | 
			
		||||
As of `v2.0.5-rc`, mounts can be managed using the `Repertory Management Portal`.
 | 
			
		||||
To launch the portal, execute the following command:
 | 
			
		||||
 | 
			
		||||
* `repertory -ui`
 | 
			
		||||
  * The default username is `repertory`
 | 
			
		||||
  * The default password is `repertory`
 | 
			
		||||
 | 
			
		||||
After first launch, `ui.json` will be created in the appropriate data directory.
 | 
			
		||||
See [Data Directories](#data-directories).
 | 
			
		||||
You should modify this file directly or use the portal to change the default
 | 
			
		||||
username and password.
 | 
			
		||||
 | 
			
		||||
### Screenshot
 | 
			
		||||
 
 | 
			
		||||
<a href="https://ibb.co/fVyJqnbF"><img src="https://i.ibb.co/fVyJqnbF/repertory-portal.png" alt="repertory-portal" border="0"></a>
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
### Important Options
 | 
			
		||||
 | 
			
		||||
* `--help`
 | 
			
		||||
  * Display all mount utility options
 | 
			
		||||
 | 
			
		||||
* `-f`
 | 
			
		||||
  * Keep process in foreground on Linux.
 | 
			
		||||
 | 
			
		||||
* `--name, -na [name]`
 | 
			
		||||
  * Identifies a unique configuration name to support multiple mounts.
 | 
			
		||||
  * The `--name` option can be set to any valid value allowed as a file name for your filesystem.
 | 
			
		||||
  * For Sia, the bucket name will be set to the same value if it is empty in the configuration file.
 | 
			
		||||
    * If the `--name` option is not specified, `default` will be used.
 | 
			
		||||
  * For S3, the `--name` option is required and does not affect the bucket name.
 | 
			
		||||
 | 
			
		||||
* `-dc`
 | 
			
		||||
  * Display mount configuration
 | 
			
		||||
  * For Sia, `--name` is optional
 | 
			
		||||
  * For S3, the `-s3` option is required along with `--name`
 | 
			
		||||
 | 
			
		||||
### Sia
 | 
			
		||||
 | 
			
		||||
* Initial Configuration
 | 
			
		||||
  * Sia steps:
 | 
			
		||||
#### Sia Initial Configuration
 | 
			
		||||
 | 
			
		||||
* Required steps:
 | 
			
		||||
  * Set the appropriate bucket name and `renterd` API password in `repertory` configuration:
 | 
			
		||||
      * To use `default` as the bucket name and configuration name:
 | 
			
		||||
    * To use `default` as the bucket name and configuration name, you only need to set the `renterd` API password:
 | 
			
		||||
      * `repertory -set HostConfig.ApiPassword '<my password>'`
 | 
			
		||||
      * To use a different bucket name with `default` as the configuration name:
 | 
			
		||||
    * To specify a different bucket name while using `default` as the configuration name:
 | 
			
		||||
      * `repertory -set HostConfig.ApiPassword '<my password>'`
 | 
			
		||||
      * `repertory -set SiaConfig.Bucket '<my bucket>'`
 | 
			
		||||
    * For all other configurations:
 | 
			
		||||
      * `repertory --name '<my config name>' -set HostConfig.ApiPassword '<my password>'`
 | 
			
		||||
      * `repertory --name '<my config name>' -set SiaConfig.Bucket '<my bucket name>'`
 | 
			
		||||
 | 
			
		||||
* Optional steps:
 | 
			
		||||
  * Set a user name used during `renterd` basic authentication:
 | 
			
		||||
    * `repertory -set HostConfig.ApiUser '<my user>'`
 | 
			
		||||
    * `repertory --name '<my config name>' -set HostConfig.ApiUser '<my user>'`
 | 
			
		||||
  * Set a custom agent string (default `Sia-Agent`):
 | 
			
		||||
    * `repertory -set HostConfig.AgentString '<my agent>'`
 | 
			
		||||
    * `repertory --name '<my config name>' -set HostConfig.AgentString '<my agent>'`
 | 
			
		||||
  * Set the host name or IP of the `renterd` instance (default `localhost`):
 | 
			
		||||
    * `repertory -set HostConfig.HostNameOrIp '<my host name>'`
 | 
			
		||||
    * `repertory --name '<my config name>' -set HostConfig.HostNameOrIp '<my host name>'`
 | 
			
		||||
  * Set the `renterd` API port (default `9980`):
 | 
			
		||||
    * `repertory -set HostConfig.ApiPort 9981`
 | 
			
		||||
    * `repertory --name '<my config name>' -set HostConfig.ApiPort 9981`
 | 
			
		||||
 | 
			
		||||
* To verify/view all configuration options:
 | 
			
		||||
  * `repertory -dc`
 | 
			
		||||
  * `repertory --name '<my config name>' -dc`
 | 
			
		||||
    * Example:
 | 
			
		||||
      * `repertory --name default -dc`
 | 
			
		||||
* Mounting on Linux:
 | 
			
		||||
 | 
			
		||||
#### Sia Mounting
 | 
			
		||||
 | 
			
		||||
* Linux:
 | 
			
		||||
  * `repertory /mnt/location`
 | 
			
		||||
  * `repertory --name '<my config name>' /mnt/location`
 | 
			
		||||
    * Example:
 | 
			
		||||
      * `repertory --name default /mnt/location`
 | 
			
		||||
* Mounting on Windows:
 | 
			
		||||
 | 
			
		||||
* Windows:
 | 
			
		||||
  * `repertory t:`
 | 
			
		||||
  * `repertory --name '<my config name>' t:`
 | 
			
		||||
    * Example:
 | 
			
		||||
      * `repertory --name default t:`
 | 
			
		||||
 | 
			
		||||
#### Sia Configuration File
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
  "ApiPassword": "<random generated rpc password>",
 | 
			
		||||
  "ApiPort": 10000,
 | 
			
		||||
  "ApiUser": "repertory",
 | 
			
		||||
  "DatabaseType": "rocksdb",
 | 
			
		||||
  "DownloadTimeoutSeconds": 30,
 | 
			
		||||
  "EnableDownloadTimeout": true,
 | 
			
		||||
  "EnableDriveEvents": false,
 | 
			
		||||
  "EventLevel": "info",
 | 
			
		||||
  "EvictionDelayMinutes": 1,
 | 
			
		||||
  "EvictionUseAccessedTime": false,
 | 
			
		||||
  "HighFreqIntervalSeconds": 30,
 | 
			
		||||
  "HostConfig": {
 | 
			
		||||
    "AgentString": "Sia-Agent",
 | 
			
		||||
    "ApiPassword": "<renterd api password>",
 | 
			
		||||
    "ApiPort": 9980,
 | 
			
		||||
    "ApiUser": "",
 | 
			
		||||
    "HostNameOrIp": "localhost",
 | 
			
		||||
    "Path": "",
 | 
			
		||||
    "Protocol": "http",
 | 
			
		||||
    "TimeoutMs": 60000
 | 
			
		||||
  },
 | 
			
		||||
  "LowFreqIntervalSeconds": 3600,
 | 
			
		||||
  "MaxCacheSizeBytes": 21474836480,
 | 
			
		||||
  "MaxUploadCount": 5,
 | 
			
		||||
  "MedFreqIntervalSeconds": 120,
 | 
			
		||||
  "OnlineCheckRetrySeconds": 60,
 | 
			
		||||
  "PreferredDownloadType": "default",
 | 
			
		||||
  "RemoteMount": {
 | 
			
		||||
    "ApiPort": 20000,
 | 
			
		||||
    "ClientPoolSize": 20,
 | 
			
		||||
    "Enable": false,
 | 
			
		||||
    "EncryptionToken": ""
 | 
			
		||||
  },
 | 
			
		||||
  "RetryReadCount": 6,
 | 
			
		||||
  "RingBufferFileSize": 512,
 | 
			
		||||
  "SiaConfig": {
 | 
			
		||||
    "Bucket": "default"
 | 
			
		||||
  },
 | 
			
		||||
  "TaskWaitMs": 100,
 | 
			
		||||
  "Version": 1
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### S3
 | 
			
		||||
 | 
			
		||||
* Initial Configuration
 | 
			
		||||
  * S3 steps:
 | 
			
		||||
#### S3 Initial Configuration
 | 
			
		||||
 | 
			
		||||
* Required steps:
 | 
			
		||||
  * Set the appropriate base URL:
 | 
			
		||||
    * `repertory -s3 --name '<my config name>' -set S3Config.URL '<my url>'`
 | 
			
		||||
      * Example:
 | 
			
		||||
@@ -73,74 +211,218 @@ on Windows.
 | 
			
		||||
    * `repertory -s3 --name '<my config name>' -set S3Config.SecretKey '<my secret key>'`
 | 
			
		||||
  * For Sia and most local S3 gateway instances, enable path style URL's:
 | 
			
		||||
    * `repertory -s3 --name '<my config name>' -set S3Config.UsePathStyle true`
 | 
			
		||||
 | 
			
		||||
* Optional steps:
 | 
			
		||||
  * Set an appropriate region. Default is set to `any`:
 | 
			
		||||
    * `repertory -s3 --name '<my config name>' -set S3Config.Region '<my region>'`
 | 
			
		||||
  * Enable encrypted file names and file data. Set a strong, random encryption token and be sure to store it in a secure backup location:
 | 
			
		||||
    * `repertory -s3 --name '<my config name>' -set S3Config.EncryptionToken '<my strong password>'`
 | 
			
		||||
 | 
			
		||||
* To verify/view all configuration options:
 | 
			
		||||
  * `repertory -s3 --name '<my config name>' -dc`
 | 
			
		||||
    * Example:
 | 
			
		||||
      * `repertory -s3 --name minio -dc`
 | 
			
		||||
* Mounting on Linux:
 | 
			
		||||
 | 
			
		||||
#### S3 Mounting
 | 
			
		||||
 | 
			
		||||
* Linux:
 | 
			
		||||
  * `repertory -s3 --name '<my config name>' /mnt/location`
 | 
			
		||||
    * Example:
 | 
			
		||||
      * `repertory -s3 --name minio /mnt/location`
 | 
			
		||||
* Mounting on Windows:
 | 
			
		||||
 | 
			
		||||
* Windows:
 | 
			
		||||
  * `repertory -s3 --name '<my config name>' t:`
 | 
			
		||||
    * Example:
 | 
			
		||||
      * `repertory -s3 --name minio t:`
 | 
			
		||||
 | 
			
		||||
### Notable Options
 | 
			
		||||
#### S3 Configuration File
 | 
			
		||||
 | 
			
		||||
* `--help`
 | 
			
		||||
  * Display all mount utility options
 | 
			
		||||
* `--name, -na [name]`
 | 
			
		||||
  * The `--name` option can be set to any valid value allowed as a file name for your filesystem.
 | 
			
		||||
  * For Sia, the bucket name will be set to the same value if it is empty in the configuration file.
 | 
			
		||||
    * If the `--name` option is not specified, `default` will be used.
 | 
			
		||||
  * For S3, the `--name` option is required and does not affect the bucket name.
 | 
			
		||||
* `-dc`
 | 
			
		||||
  * Display mount configuration
 | 
			
		||||
  * For Sia, `--name` is optional
 | 
			
		||||
  * For S3, the `-s3` option is required along with `--name`
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
  "ApiPassword": "<random generated rpc password>",
 | 
			
		||||
  "ApiPort": 10100,
 | 
			
		||||
  "ApiUser": "repertory",
 | 
			
		||||
  "DatabaseType": "rocksdb",
 | 
			
		||||
  "DownloadTimeoutSeconds": 30,
 | 
			
		||||
  "EnableDownloadTimeout": true,
 | 
			
		||||
  "EnableDriveEvents": false,
 | 
			
		||||
  "EventLevel": "info",
 | 
			
		||||
  "EvictionDelayMinutes": 1,
 | 
			
		||||
  "EvictionUseAccessedTime": false,
 | 
			
		||||
  "HighFreqIntervalSeconds": 30,
 | 
			
		||||
  "LowFreqIntervalSeconds": 3600,
 | 
			
		||||
  "MaxCacheSizeBytes": 21474836480,
 | 
			
		||||
  "MaxUploadCount": 5,
 | 
			
		||||
  "MedFreqIntervalSeconds": 120,
 | 
			
		||||
  "OnlineCheckRetrySeconds": 60,
 | 
			
		||||
  "PreferredDownloadType": "default",
 | 
			
		||||
  "RemoteMount": {
 | 
			
		||||
    "ApiPort": 20100,
 | 
			
		||||
    "ClientPoolSize": 20,
 | 
			
		||||
    "Enable": false,
 | 
			
		||||
    "EncryptionToken": ""
 | 
			
		||||
  },
 | 
			
		||||
  "RetryReadCount": 6,
 | 
			
		||||
  "RingBufferFileSize": 512,
 | 
			
		||||
  "S3Config": {
 | 
			
		||||
    "AccessKey": "<my access key>",
 | 
			
		||||
    "Bucket": "<my bucket name>",
 | 
			
		||||
    "EncryptionToken": "",
 | 
			
		||||
    "Region": "any",
 | 
			
		||||
    "SecretKey": "<my secret key>",
 | 
			
		||||
    "TimeoutMs": 60000,
 | 
			
		||||
    "URL": "http://localhost:9000",
 | 
			
		||||
    "UsePathStyle": true,
 | 
			
		||||
    "UseRegionInURL": false
 | 
			
		||||
  },
 | 
			
		||||
  "TaskWaitMs": 100,
 | 
			
		||||
  "Version": 1
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Data Directories
 | 
			
		||||
 | 
			
		||||
* Linux
 | 
			
		||||
  * `~/.local/repertory2`
 | 
			
		||||
* Windows
 | 
			
		||||
  * `%LOCALAPPDATA%\repertory2`
 | 
			
		||||
    * Example:
 | 
			
		||||
      * `C:\Users\Tom\AppData\Local\repertory2`
 | 
			
		||||
#### Linux Directories
 | 
			
		||||
 | 
			
		||||
* `~/.local/repertory2/s3`
 | 
			
		||||
* `~/.local/repertory2/sia`
 | 
			
		||||
 | 
			
		||||
#### Windows Directories
 | 
			
		||||
 | 
			
		||||
* `%LOCALAPPDATA%\repertory2\s3`
 | 
			
		||||
* `%LOCALAPPDATA%\repertory2\sia`
 | 
			
		||||
  * Examples:
 | 
			
		||||
    * `C:\Users\Tom\AppData\Local\repertory2\s3`
 | 
			
		||||
    * `C:\Users\Tom\AppData\Local\repertory2\sia`
 | 
			
		||||
* IMPORTANT:
 | 
			
		||||
  * It is highly recommended to exclude this folder from any anti-virus/anti-malware applications as severe performance issues may arise.
 | 
			
		||||
  * Excluding the mounted drive letter is also highly recommended.
 | 
			
		||||
 | 
			
		||||
## Remote Mounting
 | 
			
		||||
 | 
			
		||||
`Repertory` allows local mounts to be shared with other computers on your network.
 | 
			
		||||
This option is referred to as remote mounting. Instructions TBD.
 | 
			
		||||
`repertory` allows local mounts to be shared with other computers on your network
 | 
			
		||||
or over the internet. This option is referred to as remote mounting.
 | 
			
		||||
 | 
			
		||||
### Server Setup
 | 
			
		||||
 | 
			
		||||
The following steps must be performed on the mount you wish to share with
 | 
			
		||||
other systems. Changes to configuration will not take affect while a mount is
 | 
			
		||||
active, so it is recommended to unmount beforehand.
 | 
			
		||||
 | 
			
		||||
* Required steps:
 | 
			
		||||
  * Enable remote mount:
 | 
			
		||||
    * Sia
 | 
			
		||||
      * `repertory -set RemoteMount.Enable true`
 | 
			
		||||
      * `repertory --name '<my config name>' -set RemoteMount.Enable true`
 | 
			
		||||
    * S3:
 | 
			
		||||
      * `repertory -s3 --name '<my config name>' -set RemoteMount.Enable true`
 | 
			
		||||
  * Set a secure encryption token:
 | 
			
		||||
    * Sia:
 | 
			
		||||
      * `repertory -set RemoteMount.EncryptionToken '<my secure password>'`
 | 
			
		||||
      * `repertory --name '<my config name>' -set RemoteMount.EncryptionToken '<my secure password>'`
 | 
			
		||||
    * S3:
 | 
			
		||||
      * `repertory -s3 --name '<my config name>' -set RemoteMount.EncryptionToken '<my secure password>'`
 | 
			
		||||
 | 
			
		||||
* Optional steps:
 | 
			
		||||
  * Change the port clients will use to connect to your mount:
 | 
			
		||||
    * Sia:
 | 
			
		||||
      * `repertory -set RemoteMount.ApiPort 20000`
 | 
			
		||||
      * `repertory --name '<my config name>' -set RemoteMount.ApiPort 20000`
 | 
			
		||||
    * S3:
 | 
			
		||||
      * `repertory -s3 --name '<my config name>' -set RemoteMount.ApiPort 20000`
 | 
			
		||||
 | 
			
		||||
* IMPORTANT:
 | 
			
		||||
  * Be sure to configure your firewall to allow incoming TCP connections on the port configured in `RemoteMount.ApiPort`.
 | 
			
		||||
 | 
			
		||||
#### Remote Mount Configuration File Section
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
  ...
 | 
			
		||||
  "RemoteMount": {
 | 
			
		||||
    "ApiPort": 20000,
 | 
			
		||||
    "ClientPoolSize": 20,
 | 
			
		||||
    "Enable": true,
 | 
			
		||||
    "EncryptionToken": "<my secure password>"
 | 
			
		||||
  },
 | 
			
		||||
  ...
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Client Setup
 | 
			
		||||
 | 
			
		||||
Client configuration is provider agnostic, so there's no need to specify `-s3`
 | 
			
		||||
for S3 providers.
 | 
			
		||||
 | 
			
		||||
* Required steps:
 | 
			
		||||
  * Set the encryption token to the same value configured during server setup:
 | 
			
		||||
    * `repertory -rm <host name or IP>:<port> -set RemoteConfig.EncryptionToken '<my secure password>'`
 | 
			
		||||
      * Replace `<host name or IP>` with the host name or IP of the server
 | 
			
		||||
      * Replace `<port>` with the value of `RemoteMount.ApiPort` used in the server configuration
 | 
			
		||||
    * Example:
 | 
			
		||||
      * `repertory -rm 192.168.1.10:20000 -set RemoteConfig.EncryptionToken '<my secure password>'`
 | 
			
		||||
      * `repertory -rm my.host.com:20000 -set RemoteConfig.EncryptionToken '<my secure password>'`
 | 
			
		||||
 | 
			
		||||
#### Client Remote Mounting
 | 
			
		||||
 | 
			
		||||
* Linux:
 | 
			
		||||
  * `repertory -rm <host name or IP>:<port> /mnt/location`
 | 
			
		||||
    * Example:
 | 
			
		||||
      * `repertory -rm 192.168.1.10:20000 /mnt/location`
 | 
			
		||||
 | 
			
		||||
* Windows:
 | 
			
		||||
  * `repertory -rm <host name or IP>:<port> t:`
 | 
			
		||||
    * Example:
 | 
			
		||||
      * `repertory -rm 192.168.1.10:20000 t:`
 | 
			
		||||
 | 
			
		||||
#### Remote Mount Configuration File
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
  "ApiPassword": "<random generated rpc password>",
 | 
			
		||||
  "ApiPort": 10010,
 | 
			
		||||
  "ApiUser": "repertory",
 | 
			
		||||
  "EnableDriveEvents": false,
 | 
			
		||||
  "EventLevel": "info",
 | 
			
		||||
  "RemoteConfig": {
 | 
			
		||||
    "ApiPort": 20000,
 | 
			
		||||
    "EncryptionToken": "<my secure password>",
 | 
			
		||||
    "HostNameOrIp": "192.168.1.10",
 | 
			
		||||
    "MaxConnections": 20,
 | 
			
		||||
    "ReceiveTimeoutMs": 120000,
 | 
			
		||||
    "SendTimeoutMs": 30000
 | 
			
		||||
  },
 | 
			
		||||
  "TaskWaitMs": 100,
 | 
			
		||||
  "Version": 1
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Compiling
 | 
			
		||||
 | 
			
		||||
* Successful compilation will result in all required files being placed in the `dist/` directory
 | 
			
		||||
* Linux
 | 
			
		||||
Successful compilation will result in all files required for execution to be placed
 | 
			
		||||
in the `dist/` directory
 | 
			
		||||
 | 
			
		||||
### Linux Compilation
 | 
			
		||||
 | 
			
		||||
* Ensure `docker` is installed
 | 
			
		||||
  * For x86_64:
 | 
			
		||||
    * RelWithDebInfo: `scripts/make_unix.sh`
 | 
			
		||||
    * Release: `scripts/make_unix.sh x86_64 Release`
 | 
			
		||||
    * Debug: `scripts/make_unix.sh x86_64 Debug`
 | 
			
		||||
 | 
			
		||||
  * For aarch64:
 | 
			
		||||
    * RelWithDebInfo: `scripts/make_unix.sh aarch64`
 | 
			
		||||
    * Release: `scripts/make_unix.sh aarch64 Release`
 | 
			
		||||
    * Debug: `scripts/make_unix.sh aarch64 Debug`
 | 
			
		||||
* Windows
 | 
			
		||||
 | 
			
		||||
### Windows Compilation
 | 
			
		||||
 | 
			
		||||
* OFFICIAL: Cross-compiling on Linux
 | 
			
		||||
  * Ensure `docker` is installed
 | 
			
		||||
    * RelWithDebInfo: `scripts/make_win32.sh`
 | 
			
		||||
    * Release: `scripts/make_win32.sh x86_64 Release`
 | 
			
		||||
    * Debug: `scripts/make_win32.sh x86_64 Debug`
 | 
			
		||||
 | 
			
		||||
* UNOFFICIAL: Compiling on Windows
 | 
			
		||||
  * Ensure latest [MSYS2](https://www.msys2.org/) is installed
 | 
			
		||||
    * RelWithDebInfo: `scripts\make_win32.cmd`
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,20 @@
 | 
			
		||||
set(BINUTILS_HASH b53606f443ac8f01d1d5fc9c39497f2af322d99e14cea5c0b4b124d630379365)
 | 
			
		||||
set(BINUTILS_HASH ce2017e059d63e67ddb9240e9d4ec49c2893605035cd60e92ad53177f4377237)
 | 
			
		||||
set(BOOST2_HASH 7bd7ddceec1a1dfdcbdb3e609b60d01739c38390a5f956385a12f3122049f0ca)
 | 
			
		||||
set(BOOST_HASH f55c340aa49763b1925ccf02b2e83f35fdcf634c9d5164a2acb87540173c741d)
 | 
			
		||||
set(CPP_HTTPLIB_HASH 405abd8170f2a446fc8612ac635d0db5947c0d2e156e32603403a4496255ff00)
 | 
			
		||||
set(CURL_HASH 5a231145114589491fc52da118f9c7ef8abee885d1cb1ced99c7290e9a352f07)
 | 
			
		||||
set(EXPAT_HASH 372b18f6527d162fa9658f1c74d22a37429b82d822f5a1e1fc7e00f6045a06a2)
 | 
			
		||||
set(BOOST_HASH 3621533e820dcab1e8012afd583c0c73cf0f77694952b81352bf38c1488f9cb4)
 | 
			
		||||
set(CPP_HTTPLIB_HASH 18064587e0cc6a0d5d56d619f4cbbcaba47aa5d84d86013abbd45d95c6653866)
 | 
			
		||||
set(CURL_HASH ccc5ba45d9f5320c70ffb24e5411b66ba55ea1f333bf78be0963ed90a9328699)
 | 
			
		||||
set(EXPAT_HASH 85372797ff0673a8fc4a6be16466bb5a0ca28c0dcf3c6f7ac1686b4a3ba2aabb)
 | 
			
		||||
set(GCC_HASH 7d376d445f93126dc545e2c0086d0f647c3094aae081cdb78f42ce2bc25e7293)
 | 
			
		||||
set(GTEST_HASH 7b42b4d6ed48810c5362c265a17faebe90dc2373c885e5216439d37927f02926)
 | 
			
		||||
set(GTEST_HASH 78c676fc63881529bf97bf9d45948d905a66833fbfa5318ea2cd7478cb98f399)
 | 
			
		||||
set(ICU_HASH a2c443404f00098e9e90acf29dc318e049d2dc78d9ae5f46efb261934a730ce2)
 | 
			
		||||
set(JSON_HASH 0d8ef5af7f9794e3263480193c491549b2ba6cc74bb018906202ada498a79406)
 | 
			
		||||
set(JSON_HASH 4b92eb0c06d10683f7447ce9406cb97cd4b453be18d7279320f7b2f025c10187)
 | 
			
		||||
set(LIBSODIUM_HASH 8e5aeca07a723a27bbecc3beef14b0068d37e7fc0e97f51b3f1c82d2a58005c1)
 | 
			
		||||
set(MINGW_HASH 30e5aad2c48dd318150f79cff47661232c4175876d6b4d6b270961cf2b49a48b)
 | 
			
		||||
set(OPENSSL_HASH e15dda82fe2fe8139dc2ac21a36d4ca01d5313c75f99f46c4e8a27709b7294bf)
 | 
			
		||||
set(MINGW_HASH cc41898aac4b6e8dd5cffd7331b9d9515b912df4420a3a612b5ea2955bbeed2f)
 | 
			
		||||
set(OPENSSL_HASH 344d0a79f1a9b08029b0744e2cc401a43f9c90acd1044d09a530b4885a8e9fc0)
 | 
			
		||||
set(PKG_CONFIG_HASH 6fc69c01688c9458a57eb9a1664c9aba372ccda420a02bf4429fe610e7e7d591)
 | 
			
		||||
set(PUGIXML_HASH 2f10e276870c64b1db6809050a75e11a897a8d7456c4be5c6b2e35a11168a015)
 | 
			
		||||
set(ROCKSDB_HASH 9b810c81731835fda0d4bbdb51d3199d901fa4395733ab63752d297da84c5a47)
 | 
			
		||||
set(SPDLOG_HASH 9962648c9b4f1a7bbc76fd8d9172555bad1871fdb14ff4f842ef87949682caa5)
 | 
			
		||||
set(SQLITE_HASH 77823cb110929c2bcb0f5d48e4833b5c59a8a6e40cdea3936b99e199dbbe5784)
 | 
			
		||||
set(PUGIXML_HASH 655ade57fa703fb421c2eb9a0113b5064bddb145d415dd1f88c79353d90d511a)
 | 
			
		||||
set(ROCKSDB_HASH 3fdc9ca996971c4c039959866382c4a3a6c8ade4abf888f3b2ff77153e07bf28)
 | 
			
		||||
set(SPDLOG_HASH 7a80896357f3e8e920e85e92633b14ba0f229c506e6f978578bdc35ba09e9a5d)
 | 
			
		||||
set(SQLITE_HASH 6cebd1d8403fc58c30e93939b246f3e6e58d0765a5cd50546f16c00fd805d2c3)
 | 
			
		||||
set(STDUUID_HASH b1176597e789531c38481acbbed2a6894ad419aab0979c10410d59eb0ebf40d3)
 | 
			
		||||
set(ZLIB_HASH 17e88863f3600672ab49182f217281b6fc4d3c762bde361935e436a95214d05c)
 | 
			
		||||
 
 | 
			
		||||
@@ -120,11 +120,11 @@ if(PROJECT_ENABLE_BOOST)
 | 
			
		||||
          --with-libraries=atomic,chrono,date_time,filesystem,iostreams,locale,log,program_options,random,regex,serialization,system,test,thread
 | 
			
		||||
        BUILD_COMMAND
 | 
			
		||||
          ./b2
 | 
			
		||||
          -j1
 | 
			
		||||
          -j$ENV{CMAKE_BUILD_PARALLEL_LEVEL}
 | 
			
		||||
          ${BOOST_BUILD_ARGS}
 | 
			
		||||
        INSTALL_COMMAND
 | 
			
		||||
          ./b2
 | 
			
		||||
          -j1
 | 
			
		||||
          -j$ENV{CMAKE_BUILD_PARALLEL_LEVEL}
 | 
			
		||||
          ${BOOST_BUILD_ARGS}
 | 
			
		||||
          install
 | 
			
		||||
      )
 | 
			
		||||
 
 | 
			
		||||
@@ -15,10 +15,13 @@ if(PROJECT_ENABLE_CPP_HTTPLIB)
 | 
			
		||||
      CMAKE_ARGS ${PROJECT_EXTERNAL_CMAKE_FLAGS}
 | 
			
		||||
        -DBUILD_SHARED_LIBS=${PROJECT_BUILD_SHARED_LIBS}
 | 
			
		||||
        -DBUILD_STATIC_LIBS=ON
 | 
			
		||||
        -DHTTPLIB_REQUIRE_OPENSSL=${PROJECT_ENABLE_OPENSSL}
 | 
			
		||||
        -DHTTPLIB_REQUIRE_ZLIB=ON
 | 
			
		||||
        -DHTTPLIB_REQUIRE_BROTLI=OFF
 | 
			
		||||
        -DHTTPLIB_REQUIRE_OPENSSL=ON
 | 
			
		||||
        -DHTTPLIB_REQUIRE_ZLIB=ON
 | 
			
		||||
        -DHTTPLIB_TEST=OFF
 | 
			
		||||
        -DHTTPLIB_USE_BROTLI_IF_AVAILABLE=OFF
 | 
			
		||||
        -DHTTPLIB_USE_OPENSSL_IF_AVAILABLE=ON
 | 
			
		||||
        -DHTTPLIB_USE_ZLIB_IF_AVAILABLE=ON
 | 
			
		||||
        -DOPENSSL_USE_STATIC_LIBS=${OPENSSL_USE_STATIC_LIBS}
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,8 +18,9 @@ if(PROJECT_ENABLE_CURL)
 | 
			
		||||
      URL ${PROJECT_3RD_PARTY_DIR}/curl-${CURL_VERSION}.tar.gz
 | 
			
		||||
      URL_HASH SHA256=${CURL_HASH}
 | 
			
		||||
      LIST_SEPARATOR |
 | 
			
		||||
      CMAKE_ARGS 
 | 
			
		||||
        ${PROJECT_EXTERNAL_CMAKE_FLAGS}
 | 
			
		||||
      BUILD_COMMAND 
 | 
			
		||||
        ${CMAKE_COMMAND} --build . -- -j$ENV{CMAKE_BUILD_PARALLEL_LEVEL}
 | 
			
		||||
      CMAKE_ARGS ${PROJECT_EXTERNAL_CMAKE_FLAGS}
 | 
			
		||||
        -DBUILD_CURL_EXE=OFF
 | 
			
		||||
        -DBUILD_LIBCURL_DOCS=OFF
 | 
			
		||||
        -DBUILD_MISC_DOCS=OFF
 | 
			
		||||
@@ -28,6 +29,7 @@ if(PROJECT_ENABLE_CURL)
 | 
			
		||||
        -DBUILD_STATIC_LIBS=ON
 | 
			
		||||
        -DBUILD_STATIC_LIBS=ON
 | 
			
		||||
        -DBUILD_TESTING=OFF
 | 
			
		||||
        -DCURL_BROTLI=OFF
 | 
			
		||||
        -DCURL_CA_BUNDLE=./cacert.pem
 | 
			
		||||
        -DCURL_CA_FALLBACK=ON
 | 
			
		||||
        -DCURL_DISABLE_LDAP=ON
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,8 @@ if(PROJECT_ENABLE_ROCKSDB)
 | 
			
		||||
      URL ${PROJECT_3RD_PARTY_DIR}/rocksdb-${ROCKSDB_VERSION}.tar.gz
 | 
			
		||||
      URL_HASH SHA256=${ROCKSDB_HASH}
 | 
			
		||||
      LIST_SEPARATOR |
 | 
			
		||||
      BUILD_COMMAND 
 | 
			
		||||
        ${CMAKE_COMMAND} --build . -- -j$ENV{CMAKE_BUILD_PARALLEL_LEVEL}
 | 
			
		||||
      CMAKE_ARGS ${PROJECT_EXTERNAL_CMAKE_FLAGS}
 | 
			
		||||
        -DBUILD_SHARED_LIBS=OFF
 | 
			
		||||
        -DBUILD_STATIC_LIBS=ON
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ if(PROJECT_ENABLE_SQLITE)
 | 
			
		||||
  if(PROJECT_BUILD)
 | 
			
		||||
    add_definitions(-DPROJECT_ENABLE_SQLITE)
 | 
			
		||||
    if (PROJECT_IS_MINGW AND NOT PROJECT_IS_MINGW_UNIX)
 | 
			
		||||
      pkg_check_modules(SQLITE3 REQUIRED sqlite3>=${SQLITE2_VERSION})
 | 
			
		||||
      pkg_check_modules(SQLITE3 REQUIRED sqlite3)
 | 
			
		||||
      include_directories(SYSTEM BEFORE ${SQLITE3_INCLUDE_DIRS})
 | 
			
		||||
      link_libraries(${SQLITE3_LIBRARIES})
 | 
			
		||||
    else()
 | 
			
		||||
 
 | 
			
		||||
@@ -1,28 +1,28 @@
 | 
			
		||||
set(BINUTILS_VERSION 2.43)
 | 
			
		||||
set(BINUTILS_VERSION 2.44)
 | 
			
		||||
set(BOOST2_MAJOR_VERSION 1)
 | 
			
		||||
set(BOOST2_MINOR_VERSION 76)
 | 
			
		||||
set(BOOST2_PATCH_VERSION 0)
 | 
			
		||||
set(BOOST_MAJOR_VERSION 1)
 | 
			
		||||
set(BOOST_MINOR_VERSION 87)
 | 
			
		||||
set(BOOST_MINOR_VERSION 88)
 | 
			
		||||
set(BOOST_PATCH_VERSION 0)
 | 
			
		||||
set(CPP_HTTPLIB_VERSION 0.18.1)
 | 
			
		||||
set(CURL2_VERSION 8_11_0)
 | 
			
		||||
set(CURL_VERSION 8.11.0)
 | 
			
		||||
set(EXPAT2_VERSION 2_6_4)
 | 
			
		||||
set(EXPAT_VERSION 2.6.4)
 | 
			
		||||
set(CPP_HTTPLIB_VERSION 0.20.0)
 | 
			
		||||
set(CURL2_VERSION 8_13_0)
 | 
			
		||||
set(CURL_VERSION 8.13.0)
 | 
			
		||||
set(EXPAT2_VERSION 2_7_1)
 | 
			
		||||
set(EXPAT_VERSION 2.7.1)
 | 
			
		||||
set(GCC_VERSION 14.2.0)
 | 
			
		||||
set(GTEST_VERSION 1.15.2)
 | 
			
		||||
set(GTEST_VERSION 1.16.0)
 | 
			
		||||
set(ICU_VERSION 76-1)
 | 
			
		||||
set(JSON_VERSION 3.11.3)
 | 
			
		||||
set(JSON_VERSION 3.12.0)
 | 
			
		||||
set(LIBSODIUM_VERSION 1.0.20)
 | 
			
		||||
set(MESA_VERSION 23.3.3)
 | 
			
		||||
set(MINGW_VERSION 12.0.0)
 | 
			
		||||
set(OPENSSL_VERSION 3.4.0)
 | 
			
		||||
set(OPENSSL_VERSION 3.5.0)
 | 
			
		||||
set(PKG_CONFIG_VERSION 0.29.2)
 | 
			
		||||
set(PUGIXML_VERSION 1.14)
 | 
			
		||||
set(ROCKSDB_VERSION 9.7.4)
 | 
			
		||||
set(SPDLOG_VERSION 1.15.0)
 | 
			
		||||
set(SQLITE2_VERSION 3.46.1)
 | 
			
		||||
set(SQLITE_VERSION 3460100)
 | 
			
		||||
set(PUGIXML_VERSION 1.15)
 | 
			
		||||
set(ROCKSDB_VERSION 10.0.1)
 | 
			
		||||
set(SPDLOG_VERSION 1.15.2)
 | 
			
		||||
set(SQLITE2_VERSION 3.49.1)
 | 
			
		||||
set(SQLITE_VERSION 3490100)
 | 
			
		||||
set(STDUUID_VERSION 1.2.3)
 | 
			
		||||
set(ZLIB_VERSION 1.3.1)
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ PROJECT_DESC="Mount utility for Sia and S3"
 | 
			
		||||
 | 
			
		||||
PROJECT_MAJOR_VERSION=2
 | 
			
		||||
PROJECT_MINOR_VERSION=0
 | 
			
		||||
PROJECT_REVISION_VERSION=4
 | 
			
		||||
PROJECT_REVISION_VERSION=6
 | 
			
		||||
PROJECT_RELEASE_NUM=0
 | 
			
		||||
PROJECT_RELEASE_ITER=rc
 | 
			
		||||
 | 
			
		||||
@@ -19,6 +19,9 @@ PROJECT_APP_LIST=(${PROJECT_NAME})
 | 
			
		||||
PROJECT_PRIVATE_KEY=${DEVELOPER_PRIVATE_KEY}
 | 
			
		||||
PROJECT_PUBLIC_KEY=${DEVELOPER_PUBLIC_KEY}
 | 
			
		||||
 | 
			
		||||
PROJECT_FLUTTER_BASE_HREF="/ui/"
 | 
			
		||||
 | 
			
		||||
PROJECT_ENABLE_V2_ERRORS=ON
 | 
			
		||||
PROJECT_ENABLE_WIN32_LONG_PATH_NAMES=OFF
 | 
			
		||||
 | 
			
		||||
PROJECT_ENABLE_BACKWARD_CPP=OFF
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@ RUN apk add \
 | 
			
		||||
  gflags \
 | 
			
		||||
  gflags-dev \
 | 
			
		||||
  git \
 | 
			
		||||
  git-lfs \
 | 
			
		||||
  icu-dev \
 | 
			
		||||
  icu-libs \
 | 
			
		||||
  icu-static \
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@ RUN apk add \
 | 
			
		||||
  gflags \
 | 
			
		||||
  gflags-dev \
 | 
			
		||||
  git \
 | 
			
		||||
  git-lfs \
 | 
			
		||||
  icu-dev \
 | 
			
		||||
  icu-libs \
 | 
			
		||||
  icu-static \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										48
									
								
								docker/x86_64/flutter
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								docker/x86_64/flutter
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
FROM debian:latest
 | 
			
		||||
 | 
			
		||||
ARG UID=0
 | 
			
		||||
ARG GID=0
 | 
			
		||||
 | 
			
		||||
RUN apt-get update 
 | 
			
		||||
RUN apt-get install -y \
 | 
			
		||||
  bash \
 | 
			
		||||
  curl \
 | 
			
		||||
  fonts-droid-fallback \
 | 
			
		||||
  gdb \
 | 
			
		||||
  git \
 | 
			
		||||
  lib32stdc++6 \
 | 
			
		||||
  libgconf-2-4 \
 | 
			
		||||
  libglu1-mesa \
 | 
			
		||||
  libstdc++6 \
 | 
			
		||||
  python3 \
 | 
			
		||||
  unzip \
 | 
			
		||||
  wget
 | 
			
		||||
RUN apt-get clean
 | 
			
		||||
 | 
			
		||||
RUN git clone https://github.com/flutter/flutter.git /flutter
 | 
			
		||||
RUN git config --system --add safe.directory /flutter
 | 
			
		||||
 | 
			
		||||
ENV PATH="/flutter/bin:/flutter/bin/cache/dart-sdk/bin:${PATH}"
 | 
			
		||||
 | 
			
		||||
RUN flutter doctor -v
 | 
			
		||||
RUN flutter channel master
 | 
			
		||||
RUN flutter upgrade
 | 
			
		||||
RUN flutter --disable-analytics
 | 
			
		||||
 | 
			
		||||
RUN flutter config --no-analytics
 | 
			
		||||
RUN flutter config --enable-web
 | 
			
		||||
RUN flutter config --no-cli-animations
 | 
			
		||||
 | 
			
		||||
RUN mkdir /nonexistent
 | 
			
		||||
RUN chown -R $UID:$GID /nonexistent
 | 
			
		||||
 | 
			
		||||
RUN mkdir /.config
 | 
			
		||||
RUN chown -R $UID:$GID /.config
 | 
			
		||||
 | 
			
		||||
RUN mkdir /.dart-tool
 | 
			
		||||
RUN chown -R $UID:$GID /.dart-tool
 | 
			
		||||
 | 
			
		||||
RUN mkdir /.pub-cache
 | 
			
		||||
RUN chown -R $UID:$GID /.pub-cache
 | 
			
		||||
 | 
			
		||||
RUN chown -R $UID:$GID /flutter
 | 
			
		||||
@@ -18,6 +18,7 @@ RUN apk add \
 | 
			
		||||
  gcc \
 | 
			
		||||
  gettext \
 | 
			
		||||
  git \
 | 
			
		||||
  git-lfs \
 | 
			
		||||
  gmp \
 | 
			
		||||
  gmp-dev \
 | 
			
		||||
  gperf \
 | 
			
		||||
@@ -350,9 +351,9 @@ RUN cd /3rd_party/mingw64 && sha256sum -c ./expat-${MY_EXPAT_VERSION}.tar.gz.sha
 | 
			
		||||
 | 
			
		||||
ARG FONTCONFIG_VERSION
 | 
			
		||||
ENV MY_FONTCONFIG_VERSION=${FONTCONFIG_VERSION}
 | 
			
		||||
RUN if [ -f "/3rd_party/fontconfig-${MY_FONTCONFIG_VERSION}.tar.gz" ]; then \
 | 
			
		||||
      cd /3rd_party && sha256sum -c ./fontconfig-${MY_FONTCONFIG_VERSION}.tar.gz.sha256 && cd - \
 | 
			
		||||
      && tar xvzf /3rd_party/fontconfig-${MY_FONTCONFIG_VERSION}.tar.gz \
 | 
			
		||||
RUN if [ -f "/3rd_party/fontconfig-${MY_FONTCONFIG_VERSION}.tar.xz" ]; then \
 | 
			
		||||
      cd /3rd_party && sha256sum -c ./fontconfig-${MY_FONTCONFIG_VERSION}.tar.xz.sha256 && cd - \
 | 
			
		||||
      && tar xvJf /3rd_party/fontconfig-${MY_FONTCONFIG_VERSION}.tar.xz \
 | 
			
		||||
      && cd fontconfig-${MY_FONTCONFIG_VERSION} \
 | 
			
		||||
      && meson setup \
 | 
			
		||||
        --cross-file ${MY_TOOLCHAIN_FILE_MESON} \
 | 
			
		||||
@@ -679,6 +680,7 @@ RUN if [ -f "/3rd_party/curl-${MY_CURL_VERSION}.tar.gz" ]; then \
 | 
			
		||||
        -DCMAKE_CXX_STANDARD=${MY_CXX_STANDARD} \
 | 
			
		||||
        -DCMAKE_INSTALL_PREFIX=${MY_MINGW_DIR} \
 | 
			
		||||
        -DCMAKE_TOOLCHAIN_FILE=${MY_TOOLCHAIN_FILE_CMAKE} \
 | 
			
		||||
        -DCURL_BROTLI=OFF \
 | 
			
		||||
        -DCURL_CA_BUNDLE=./cacert.pem \
 | 
			
		||||
        -DCURL_CA_FALLBACK=ON \
 | 
			
		||||
        -DCURL_DISABLE_LDAP=ON \
 | 
			
		||||
@@ -714,6 +716,9 @@ RUN if [ -f "/3rd_party/cpp-httplib-${MY_CPP_HTTPLIB_VERSION}.tar.gz" ]; then \
 | 
			
		||||
        -DHTTPLIB_REQUIRE_OPENSSL=ON \
 | 
			
		||||
        -DHTTPLIB_REQUIRE_ZLIB=ON \
 | 
			
		||||
        -DHTTPLIB_TEST=OFF \
 | 
			
		||||
        -DHTTPLIB_USE_BROTLI_IF_AVAILABLE=OFF \
 | 
			
		||||
        -DHTTPLIB_USE_OPENSSL_IF_AVAILABLE=YES \
 | 
			
		||||
        -DHTTPLIB_USE_ZLIB_IF_AVAILABLE=ON \
 | 
			
		||||
      && make -j${MY_NUM_JOBS} \
 | 
			
		||||
      && make install \
 | 
			
		||||
      && cd ${MY_WORKDIR} \
 | 
			
		||||
 
 | 
			
		||||
@@ -43,8 +43,7 @@ public:
 | 
			
		||||
  [[nodiscard]] static auto default_remote_api_port(const provider_type &prov)
 | 
			
		||||
      -> std::uint16_t;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] static auto default_rpc_port(const provider_type &prov)
 | 
			
		||||
      -> std::uint16_t;
 | 
			
		||||
  [[nodiscard]] static auto default_rpc_port() -> std::uint16_t;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] static auto get_provider_display_name(const provider_type &prov)
 | 
			
		||||
      -> std::string;
 | 
			
		||||
@@ -52,6 +51,8 @@ public:
 | 
			
		||||
  [[nodiscard]] static auto get_provider_name(const provider_type &prov)
 | 
			
		||||
      -> std::string;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] static auto get_root_data_directory() -> std::string;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  [[nodiscard]] static auto get_stop_requested() -> bool;
 | 
			
		||||
 | 
			
		||||
@@ -71,7 +72,7 @@ public:
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  provider_type prov_;
 | 
			
		||||
  atomic<std::string> api_auth_;
 | 
			
		||||
  atomic<std::string> api_password_;
 | 
			
		||||
  std::atomic<std::uint16_t> api_port_;
 | 
			
		||||
  atomic<std::string> api_user_;
 | 
			
		||||
  std::atomic<bool> config_changed_;
 | 
			
		||||
@@ -121,7 +122,7 @@ private:
 | 
			
		||||
  auto set_value(dest &dst, const source &src) -> bool;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  [[nodiscard]] auto get_api_auth() const -> std::string;
 | 
			
		||||
  [[nodiscard]] auto get_api_password() const -> std::string;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_api_port() const -> std::uint16_t;
 | 
			
		||||
 | 
			
		||||
@@ -199,7 +200,7 @@ public:
 | 
			
		||||
 | 
			
		||||
  void save();
 | 
			
		||||
 | 
			
		||||
  void set_api_auth(const std::string &value);
 | 
			
		||||
  void set_api_password(const std::string &value);
 | 
			
		||||
 | 
			
		||||
  void set_api_port(std::uint16_t value);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -225,8 +225,7 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (curl_code != CURLE_OK) {
 | 
			
		||||
      event_system::instance().raise<curl_error>(curl_code, function_name,
 | 
			
		||||
                                                  url);
 | 
			
		||||
      event_system::instance().raise<curl_error>(curl_code, function_name, url);
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -54,12 +54,13 @@ REPERTORY_IGNORE_WARNINGS_DISABLE()
 | 
			
		||||
using namespace std::chrono_literals;
 | 
			
		||||
using json = nlohmann::json;
 | 
			
		||||
 | 
			
		||||
inline constexpr const std::string_view REPERTORY = "repertory";
 | 
			
		||||
inline constexpr const std::wstring_view REPERTORY_W = L"repertory";
 | 
			
		||||
inline constexpr const std::string_view REPERTORY{"repertory"};
 | 
			
		||||
inline constexpr const std::string_view REPERTORY_DATA_NAME{"repertory2"};
 | 
			
		||||
inline constexpr const std::wstring_view REPERTORY_W{L"repertory"};
 | 
			
		||||
 | 
			
		||||
inline constexpr const std::uint64_t REPERTORY_CONFIG_VERSION = 1ULL;
 | 
			
		||||
inline constexpr const std::string_view REPERTORY_DATA_NAME = "repertory2";
 | 
			
		||||
inline constexpr const std::string_view REPERTORY_MIN_REMOTE_VERSION = "2.0.0";
 | 
			
		||||
inline constexpr const std::uint64_t REPERTORY_CONFIG_VERSION{2ULL};
 | 
			
		||||
inline constexpr const std::string_view REPERTORY_MIN_REMOTE_VERSION{"2.0.0"};
 | 
			
		||||
inline constexpr const std::string_view RENTERD_MIN_VERSION{"2.0.0"};
 | 
			
		||||
 | 
			
		||||
#define REPERTORY_INVALID_HANDLE INVALID_HANDLE_VALUE
 | 
			
		||||
 | 
			
		||||
@@ -221,7 +222,6 @@ using WCHAR = wchar_t;
 | 
			
		||||
 | 
			
		||||
#define MAX_PATH 260
 | 
			
		||||
 | 
			
		||||
#define STATUS_SUCCESS std::uint32_t{0U}
 | 
			
		||||
#define STATUS_ACCESS_DENIED std::uint32_t{0xC0000022L}
 | 
			
		||||
#define STATUS_DEVICE_BUSY std::uint32_t{0x80000011L}
 | 
			
		||||
#define STATUS_DEVICE_INSUFFICIENT_RESOURCES std::uint32_t{0xC0000468L}
 | 
			
		||||
@@ -234,11 +234,13 @@ using WCHAR = wchar_t;
 | 
			
		||||
#define STATUS_INVALID_HANDLE std::uint32_t{0xC0000006L}
 | 
			
		||||
#define STATUS_INVALID_IMAGE_FORMAT std::uint32_t{0xC000007BL}
 | 
			
		||||
#define STATUS_INVALID_PARAMETER std::uint32_t{0xC000000DL}
 | 
			
		||||
#define STATUS_NO_MEMORY std::uint32_t{0xC0000017L}
 | 
			
		||||
#define STATUS_NOT_IMPLEMENTED std::uint32_t{0xC0000002L}
 | 
			
		||||
#define STATUS_NO_MEMORY std::uint32_t{0xC0000017L}
 | 
			
		||||
#define STATUS_OBJECT_NAME_COLLISION std::uint32_t{0xC0000035L}
 | 
			
		||||
#define STATUS_OBJECT_NAME_EXISTS std::uint32_t{0x40000000L}
 | 
			
		||||
#define STATUS_OBJECT_NAME_NOT_FOUND std::uint32_t{0xC0000034L}
 | 
			
		||||
#define STATUS_OBJECT_PATH_INVALID std::uint32_t{0xC0000039L}
 | 
			
		||||
#define STATUS_SUCCESS std::uint32_t{0U}
 | 
			
		||||
#define STATUS_UNEXPECTED_IO_ERROR std::uint32_t{0xC00000E9L}
 | 
			
		||||
 | 
			
		||||
#define CONVERT_STATUS_NOT_IMPLEMENTED(e)                                      \
 | 
			
		||||
 
 | 
			
		||||
@@ -73,92 +73,95 @@ private:
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
#if defined(__APPLE__)
 | 
			
		||||
  [[nodiscard]] auto chflags_impl(std::string api_path,
 | 
			
		||||
                                  uint32_t flags) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto chflags_impl(std::string api_path, uint32_t flags)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#endif // __APPLE__
 | 
			
		||||
 | 
			
		||||
#if FUSE_USE_VERSION >= 30
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  chmod_impl(std::string api_path, mode_t mode,
 | 
			
		||||
             struct fuse_file_info *file_info) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto chmod_impl(std::string api_path, mode_t mode,
 | 
			
		||||
                                struct fuse_file_info *file_info)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#else
 | 
			
		||||
  [[nodiscard]] auto chmod_impl(std::string api_path,
 | 
			
		||||
                                mode_t mode) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto chmod_impl(std::string api_path, mode_t mode)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if FUSE_USE_VERSION >= 30
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  chown_impl(std::string api_path, uid_t uid, gid_t gid,
 | 
			
		||||
             struct fuse_file_info *file_info) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto chown_impl(std::string api_path, uid_t uid, gid_t gid,
 | 
			
		||||
                                struct fuse_file_info *file_info)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#else
 | 
			
		||||
  [[nodiscard]] auto chown_impl(std::string api_path, uid_t uid,
 | 
			
		||||
                                gid_t gid) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto chown_impl(std::string api_path, uid_t uid, gid_t gid)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  create_impl(std::string api_path, mode_t mode,
 | 
			
		||||
              struct fuse_file_info *file_info) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto create_impl(std::string api_path, mode_t mode,
 | 
			
		||||
                                 struct fuse_file_info *file_info)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
 | 
			
		||||
  void destroy_impl(void *ptr) override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  fallocate_impl(std::string api_path, int mode, off_t offset, off_t length,
 | 
			
		||||
                 struct fuse_file_info *file_info) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto fallocate_impl(std::string api_path, int mode,
 | 
			
		||||
                                    off_t offset, off_t length,
 | 
			
		||||
                                    struct fuse_file_info *file_info)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  fgetattr_impl(std::string api_path, struct stat *unix_st,
 | 
			
		||||
                struct fuse_file_info *file_info) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto fgetattr_impl(std::string api_path, struct stat *unix_st,
 | 
			
		||||
                                   struct fuse_file_info *file_info)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__)
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  fsetattr_x_impl(std::string api_path, struct setattr_x *attr,
 | 
			
		||||
                  struct fuse_file_info *file_info) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto fsetattr_x_impl(std::string api_path,
 | 
			
		||||
                                     struct setattr_x *attr,
 | 
			
		||||
                                     struct fuse_file_info *file_info)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#endif // __APPLE__
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  fsync_impl(std::string api_path, int datasync,
 | 
			
		||||
             struct fuse_file_info *file_info) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto fsync_impl(std::string api_path, int datasync,
 | 
			
		||||
                                struct fuse_file_info *file_info)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
 | 
			
		||||
#if FUSE_USE_VERSION < 30
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  ftruncate_impl(std::string api_path, off_t size,
 | 
			
		||||
                 struct fuse_file_info *file_info) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto ftruncate_impl(std::string api_path, off_t size,
 | 
			
		||||
                                    struct fuse_file_info *file_info)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if FUSE_USE_VERSION >= 30
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  getattr_impl(std::string api_path, struct stat *unix_st,
 | 
			
		||||
               struct fuse_file_info *file_info) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto getattr_impl(std::string api_path, struct stat *unix_st,
 | 
			
		||||
                                  struct fuse_file_info *file_info)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#else
 | 
			
		||||
  [[nodiscard]] auto getattr_impl(std::string api_path,
 | 
			
		||||
                                  struct stat *unix_st) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto getattr_impl(std::string api_path, struct stat *unix_st)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__)
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  getxtimes_impl(std::string api_path, struct timespec *bkuptime,
 | 
			
		||||
                 struct timespec *crtime) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto getxtimes_impl(std::string api_path,
 | 
			
		||||
                                    struct timespec *bkuptime,
 | 
			
		||||
                                    struct timespec *crtime)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#endif // __APPLE__
 | 
			
		||||
 | 
			
		||||
#if FUSE_USE_VERSION >= 30
 | 
			
		||||
  auto init_impl(struct fuse_conn_info *conn,
 | 
			
		||||
                 struct fuse_config *cfg) -> void * override;
 | 
			
		||||
  auto init_impl(struct fuse_conn_info *conn, struct fuse_config *cfg)
 | 
			
		||||
      -> void * override;
 | 
			
		||||
#else
 | 
			
		||||
  auto init_impl(struct fuse_conn_info *conn) -> void * override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto mkdir_impl(std::string api_path,
 | 
			
		||||
                                mode_t mode) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto mkdir_impl(std::string api_path, mode_t mode)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
 | 
			
		||||
  void notify_fuse_main_exit(int &ret) override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  open_impl(std::string api_path,
 | 
			
		||||
            struct fuse_file_info *file_info) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto open_impl(std::string api_path,
 | 
			
		||||
                               struct fuse_file_info *file_info)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  opendir_impl(std::string api_path,
 | 
			
		||||
               struct fuse_file_info *file_info) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto opendir_impl(std::string api_path,
 | 
			
		||||
                                  struct fuse_file_info *file_info)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto read_impl(std::string api_path, char *buffer,
 | 
			
		||||
                               size_t read_size, off_t read_offset,
 | 
			
		||||
@@ -166,29 +169,30 @@ protected:
 | 
			
		||||
                               std::size_t &bytes_read) -> api_error override;
 | 
			
		||||
 | 
			
		||||
#if FUSE_USE_VERSION >= 30
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  readdir_impl(std::string api_path, void *buf, fuse_fill_dir_t fuse_fill_dir,
 | 
			
		||||
               off_t offset, struct fuse_file_info *file_info,
 | 
			
		||||
               fuse_readdir_flags flags) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto readdir_impl(std::string api_path, void *buf,
 | 
			
		||||
                                  fuse_fill_dir_t fuse_fill_dir, off_t offset,
 | 
			
		||||
                                  struct fuse_file_info *file_info,
 | 
			
		||||
                                  fuse_readdir_flags flags)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#else
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  readdir_impl(std::string api_path, void *buf, fuse_fill_dir_t fuse_fill_dir,
 | 
			
		||||
               off_t offset,
 | 
			
		||||
               struct fuse_file_info *file_info) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto readdir_impl(std::string api_path, void *buf,
 | 
			
		||||
                                  fuse_fill_dir_t fuse_fill_dir, off_t offset,
 | 
			
		||||
                                  struct fuse_file_info *file_info)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  release_impl(std::string api_path,
 | 
			
		||||
               struct fuse_file_info *file_info) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto release_impl(std::string api_path,
 | 
			
		||||
                                  struct fuse_file_info *file_info)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  releasedir_impl(std::string api_path,
 | 
			
		||||
                  struct fuse_file_info *file_info) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto releasedir_impl(std::string api_path,
 | 
			
		||||
                                     struct fuse_file_info *file_info)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
 | 
			
		||||
#if FUSE_USE_VERSION >= 30
 | 
			
		||||
  [[nodiscard]] auto rename_impl(std::string from_api_path,
 | 
			
		||||
                                 std::string to_api_path,
 | 
			
		||||
                                 unsigned int flags) -> api_error override;
 | 
			
		||||
                                 std::string to_api_path, unsigned int flags)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#else
 | 
			
		||||
  [[nodiscard]] auto rename_impl(std::string from_api_path,
 | 
			
		||||
                                 std::string to_api_path) -> api_error override;
 | 
			
		||||
@@ -199,8 +203,8 @@ protected:
 | 
			
		||||
#if defined(HAS_SETXATTR)
 | 
			
		||||
  [[nodiscard]] auto getxattr_common(std::string api_path, const char *name,
 | 
			
		||||
                                     char *value, size_t size,
 | 
			
		||||
                                     int &attribute_size,
 | 
			
		||||
                                     uint32_t *position) -> api_error;
 | 
			
		||||
                                     int &attribute_size, uint32_t *position)
 | 
			
		||||
      -> api_error;
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__)
 | 
			
		||||
  [[nodiscard]] auto getxattr_impl(std::string api_path, const char *name,
 | 
			
		||||
@@ -216,8 +220,8 @@ protected:
 | 
			
		||||
                                    size_t size, int &required_size,
 | 
			
		||||
                                    bool &return_size) -> api_error override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto removexattr_impl(std::string api_path,
 | 
			
		||||
                                      const char *name) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto removexattr_impl(std::string api_path, const char *name)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__)
 | 
			
		||||
  [[nodiscard]] auto setxattr_impl(std::string api_path, const char *name,
 | 
			
		||||
@@ -225,62 +229,64 @@ protected:
 | 
			
		||||
                                   uint32_t position) -> api_error override;
 | 
			
		||||
#else  // __APPLE__
 | 
			
		||||
  [[nodiscard]] auto setxattr_impl(std::string api_path, const char *name,
 | 
			
		||||
                                   const char *value, size_t size,
 | 
			
		||||
                                   int flags) -> api_error override;
 | 
			
		||||
                                   const char *value, size_t size, int flags)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#endif // __APPLE__
 | 
			
		||||
#endif // HAS_SETXATTR
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__)
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  setattr_x_impl(std::string api_path,
 | 
			
		||||
                 struct setattr_x *attr) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto setattr_x_impl(std::string api_path,
 | 
			
		||||
                                    struct setattr_x *attr)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  setbkuptime_impl(std::string api_path,
 | 
			
		||||
                   const struct timespec *bkuptime) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto setbkuptime_impl(std::string api_path,
 | 
			
		||||
                                      const struct timespec *bkuptime)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  setchgtime_impl(std::string api_path,
 | 
			
		||||
                  const struct timespec *chgtime) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto setchgtime_impl(std::string api_path,
 | 
			
		||||
                                     const struct timespec *chgtime)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  setcrtime_impl(std::string api_path,
 | 
			
		||||
                 const struct timespec *crtime) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto setcrtime_impl(std::string api_path,
 | 
			
		||||
                                    const struct timespec *crtime)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto setvolname_impl(const char *volname) -> api_error override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto statfs_x_impl(std::string api_path,
 | 
			
		||||
                                   struct statfs *stbuf) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto statfs_x_impl(std::string api_path, struct statfs *stbuf)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#else  // __APPLE__
 | 
			
		||||
  [[nodiscard]] auto statfs_impl(std::string api_path,
 | 
			
		||||
                                 struct statvfs *stbuf) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto statfs_impl(std::string api_path, struct statvfs *stbuf)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#endif // __APPLE__
 | 
			
		||||
 | 
			
		||||
#if FUSE_USE_VERSION >= 30
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  truncate_impl(std::string api_path, off_t size,
 | 
			
		||||
                struct fuse_file_info *file_info) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto truncate_impl(std::string api_path, off_t size,
 | 
			
		||||
                                   struct fuse_file_info *file_info)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#else
 | 
			
		||||
  [[nodiscard]] auto truncate_impl(std::string api_path,
 | 
			
		||||
                                   off_t size) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto truncate_impl(std::string api_path, off_t size)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto unlink_impl(std::string api_path) -> api_error override;
 | 
			
		||||
 | 
			
		||||
#if FUSE_USE_VERSION >= 30
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  utimens_impl(std::string api_path, const struct timespec tv[2],
 | 
			
		||||
               struct fuse_file_info *file_info) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto utimens_impl(std::string api_path,
 | 
			
		||||
                                  const struct timespec tv[2],
 | 
			
		||||
                                  struct fuse_file_info *file_info)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#else
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  utimens_impl(std::string api_path,
 | 
			
		||||
               const struct timespec tv[2]) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto utimens_impl(std::string api_path,
 | 
			
		||||
                                  const struct timespec tv[2])
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  write_impl(std::string api_path, const char *buffer, size_t write_size,
 | 
			
		||||
             off_t write_offset, struct fuse_file_info *file_info,
 | 
			
		||||
             std::size_t &bytes_written) -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto write_impl(std::string api_path, const char *buffer,
 | 
			
		||||
                                size_t write_size, off_t write_offset,
 | 
			
		||||
                                struct fuse_file_info *file_info,
 | 
			
		||||
                                std::size_t &bytes_written)
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  [[nodiscard]] auto get_directory_item_count(const std::string &api_path) const
 | 
			
		||||
@@ -289,16 +295,17 @@ public:
 | 
			
		||||
  [[nodiscard]] auto get_directory_items(const std::string &api_path) const
 | 
			
		||||
      -> directory_item_list override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  get_file_size(const std::string &api_path) const -> std::uint64_t override;
 | 
			
		||||
  [[nodiscard]] auto get_file_size(const std::string &api_path) const
 | 
			
		||||
      -> std::uint64_t override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  get_item_meta(const std::string &api_path,
 | 
			
		||||
                api_meta_map &meta) const -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto get_item_meta(const std::string &api_path,
 | 
			
		||||
                                   api_meta_map &meta) const
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  get_item_meta(const std::string &api_path, const std::string &name,
 | 
			
		||||
                std::string &value) const -> api_error override;
 | 
			
		||||
  [[nodiscard]] auto get_item_meta(const std::string &api_path,
 | 
			
		||||
                                   const std::string &name,
 | 
			
		||||
                                   std::string &value) const
 | 
			
		||||
      -> api_error override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_total_drive_space() const -> std::uint64_t override;
 | 
			
		||||
 | 
			
		||||
@@ -309,19 +316,22 @@ public:
 | 
			
		||||
  void get_volume_info(UINT64 &total_size, UINT64 &free_size,
 | 
			
		||||
                       std::string &volume_label) const override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  is_processing(const std::string &api_path) const -> bool override;
 | 
			
		||||
  [[nodiscard]] auto is_processing(const std::string &api_path) const
 | 
			
		||||
      -> bool override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  rename_directory(const std::string &from_api_path,
 | 
			
		||||
                   const std::string &to_api_path) -> int override;
 | 
			
		||||
  [[nodiscard]] auto rename_directory(const std::string &from_api_path,
 | 
			
		||||
                                      const std::string &to_api_path)
 | 
			
		||||
      -> int override;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto rename_file(const std::string &from_api_path,
 | 
			
		||||
                                 const std::string &to_api_path,
 | 
			
		||||
                                 bool overwrite) -> int override;
 | 
			
		||||
                                 const std::string &to_api_path, bool overwrite)
 | 
			
		||||
      -> int override;
 | 
			
		||||
 | 
			
		||||
  void set_item_meta(const std::string &api_path, const std::string &key,
 | 
			
		||||
                     const std::string &value) override;
 | 
			
		||||
 | 
			
		||||
  void set_item_meta(const std::string &api_path,
 | 
			
		||||
                     const api_meta_map &meta) override;
 | 
			
		||||
};
 | 
			
		||||
} // namespace repertory
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,29 +30,32 @@ class i_fuse_drive {
 | 
			
		||||
  INTERFACE_SETUP(i_fuse_drive);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  [[nodiscard]] virtual auto
 | 
			
		||||
  check_owner(const std::string &api_path) const -> api_error = 0;
 | 
			
		||||
  [[nodiscard]] virtual auto check_owner(const std::string &api_path) const
 | 
			
		||||
      -> api_error = 0;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] virtual auto check_parent_access(const std::string &api_path,
 | 
			
		||||
                                                 int mask) const
 | 
			
		||||
      -> api_error = 0;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] virtual auto
 | 
			
		||||
  check_parent_access(const std::string &api_path,
 | 
			
		||||
                      int mask) const -> api_error = 0;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] virtual auto get_directory_item_count(
 | 
			
		||||
      const std::string &api_path) const -> std::uint64_t = 0;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] virtual auto get_directory_items(
 | 
			
		||||
      const std::string &api_path) const -> directory_item_list = 0;
 | 
			
		||||
  get_directory_item_count(const std::string &api_path) const
 | 
			
		||||
      -> std::uint64_t = 0;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] virtual auto
 | 
			
		||||
  get_file_size(const std::string &api_path) const -> std::uint64_t = 0;
 | 
			
		||||
  get_directory_items(const std::string &api_path) const
 | 
			
		||||
      -> directory_item_list = 0;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] virtual auto
 | 
			
		||||
  get_item_meta(const std::string &api_path,
 | 
			
		||||
                api_meta_map &meta) const -> api_error = 0;
 | 
			
		||||
  [[nodiscard]] virtual auto get_file_size(const std::string &api_path) const
 | 
			
		||||
      -> std::uint64_t = 0;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] virtual auto
 | 
			
		||||
  get_item_meta(const std::string &api_path, const std::string &name,
 | 
			
		||||
                std::string &value) const -> api_error = 0;
 | 
			
		||||
  [[nodiscard]] virtual auto get_item_meta(const std::string &api_path,
 | 
			
		||||
                                           api_meta_map &meta) const
 | 
			
		||||
      -> api_error = 0;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] virtual auto get_item_meta(const std::string &api_path,
 | 
			
		||||
                                           const std::string &name,
 | 
			
		||||
                                           std::string &value) const
 | 
			
		||||
      -> api_error = 0;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] virtual auto get_total_drive_space() const -> std::uint64_t = 0;
 | 
			
		||||
 | 
			
		||||
@@ -63,12 +66,12 @@ public:
 | 
			
		||||
  virtual void get_volume_info(UINT64 &total_size, UINT64 &free_size,
 | 
			
		||||
                               std::string &volume_label) const = 0;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] virtual auto
 | 
			
		||||
  is_processing(const std::string &api_path) const -> bool = 0;
 | 
			
		||||
  [[nodiscard]] virtual auto is_processing(const std::string &api_path) const
 | 
			
		||||
      -> bool = 0;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] virtual auto
 | 
			
		||||
  rename_directory(const std::string &from_api_path,
 | 
			
		||||
                   const std::string &to_api_path) -> int = 0;
 | 
			
		||||
  [[nodiscard]] virtual auto rename_directory(const std::string &from_api_path,
 | 
			
		||||
                                              const std::string &to_api_path)
 | 
			
		||||
      -> int = 0;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] virtual auto rename_file(const std::string &from_api_path,
 | 
			
		||||
                                         const std::string &to_api_path,
 | 
			
		||||
@@ -77,6 +80,9 @@ public:
 | 
			
		||||
  virtual void set_item_meta(const std::string &api_path,
 | 
			
		||||
                             const std::string &key,
 | 
			
		||||
                             const std::string &value) = 0;
 | 
			
		||||
 | 
			
		||||
  virtual void set_item_meta(const std::string &api_path,
 | 
			
		||||
                             const api_meta_map &meta) = 0;
 | 
			
		||||
};
 | 
			
		||||
} // namespace repertory
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,8 @@ private:
 | 
			
		||||
 | 
			
		||||
  static void populate_stat(const struct stat64 &unix_st, remote::stat &r_stat);
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto update_to_windows_format(json &item) -> json &;
 | 
			
		||||
  [[nodiscard]] auto update_to_windows_format(const std::string &root_api_path,
 | 
			
		||||
                                              json &item) -> json &;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  // FUSE Layer
 | 
			
		||||
 
 | 
			
		||||
@@ -143,6 +143,7 @@ public:
 | 
			
		||||
                                     allocation_size, &file_desc, &file_info,
 | 
			
		||||
                                     normalized_name, exists);
 | 
			
		||||
           if (ret == STATUS_SUCCESS) {
 | 
			
		||||
             if (exists == 0U) {
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
               this->set_client_id(file_desc, client_id);
 | 
			
		||||
#else  // !defined(_WIN32)
 | 
			
		||||
@@ -151,6 +152,8 @@ public:
 | 
			
		||||
                       reinterpret_cast<std::uintptr_t>(file_desc)),
 | 
			
		||||
                   client_id);
 | 
			
		||||
#endif // defined(_WIN32)
 | 
			
		||||
             }
 | 
			
		||||
 | 
			
		||||
             response.encode(file_desc);
 | 
			
		||||
             response.encode(file_info);
 | 
			
		||||
             response.encode(normalized_name);
 | 
			
		||||
@@ -589,8 +592,9 @@ public:
 | 
			
		||||
           DECODE_OR_RETURN(request, flags);
 | 
			
		||||
 | 
			
		||||
           remote::file_handle handle{};
 | 
			
		||||
           if ((ret = this->fuse_create(path.data(), mode, flags, handle)) >=
 | 
			
		||||
               0) {
 | 
			
		||||
 | 
			
		||||
           ret = this->fuse_create(path.data(), mode, flags, handle);
 | 
			
		||||
           if (ret >= 0) {
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
             this->set_compat_client_id(handle, client_id);
 | 
			
		||||
#else  // !defined(_WIN32)
 | 
			
		||||
@@ -846,7 +850,8 @@ public:
 | 
			
		||||
           DECODE_OR_RETURN(request, flags);
 | 
			
		||||
 | 
			
		||||
           remote::file_handle handle;
 | 
			
		||||
           if ((ret = this->fuse_open(path.c_str(), flags, handle)) >= 0) {
 | 
			
		||||
           ret = this->fuse_open(path.c_str(), flags, handle);
 | 
			
		||||
           if (ret >= 0) {
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
             this->set_compat_client_id(handle, client_id);
 | 
			
		||||
#else  // !defined(_WIN32)
 | 
			
		||||
@@ -867,7 +872,8 @@ public:
 | 
			
		||||
           DECODE_OR_RETURN(request, path);
 | 
			
		||||
 | 
			
		||||
           remote::file_handle handle{0};
 | 
			
		||||
           if ((ret = this->fuse_opendir(path.c_str(), handle)) >= 0) {
 | 
			
		||||
           ret = this->fuse_opendir(path.c_str(), handle);
 | 
			
		||||
           if (ret >= 0) {
 | 
			
		||||
             this->add_directory(client_id, handle);
 | 
			
		||||
             response.encode(handle);
 | 
			
		||||
           }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										68
									
								
								repertory/librepertory/include/events/types/info_log.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								repertory/librepertory/include/events/types/info_log.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
/*
 | 
			
		||||
  Copyright <2018-2025> <scott.e.graves@protonmail.com>
 | 
			
		||||
 | 
			
		||||
  Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
  of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
  in the Software without restriction, including without limitation the rights
 | 
			
		||||
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
  copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
  furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
  The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
  copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
  SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
#ifndef REPERTORY_INCLUDE_EVENTS_TYPES_INFO_LOG_HPP_
 | 
			
		||||
#define REPERTORY_INCLUDE_EVENTS_TYPES_INFO_LOG_HPP_
 | 
			
		||||
 | 
			
		||||
#include "events/i_event.hpp"
 | 
			
		||||
#include "types/repertory.hpp"
 | 
			
		||||
 | 
			
		||||
namespace repertory {
 | 
			
		||||
struct info_log final : public i_event {
 | 
			
		||||
  info_log() = default;
 | 
			
		||||
  info_log(std::string_view function_name_, std::string msg_)
 | 
			
		||||
      : function_name(std::string(function_name_)), msg(std::move(msg_)) {}
 | 
			
		||||
 | 
			
		||||
  static constexpr const event_level level{event_level::info};
 | 
			
		||||
  static constexpr const std::string_view name{"info_log"};
 | 
			
		||||
 | 
			
		||||
  std::string function_name;
 | 
			
		||||
  std::string msg;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_event_level() const -> event_level override {
 | 
			
		||||
    return level;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_name() const -> std::string_view override {
 | 
			
		||||
    return name;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_single_line() const -> std::string override {
 | 
			
		||||
    return fmt::format("{}|func|{}|msg|{}", name, function_name, msg);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
} // namespace repertory
 | 
			
		||||
 | 
			
		||||
NLOHMANN_JSON_NAMESPACE_BEGIN
 | 
			
		||||
template <> struct adl_serializer<repertory::info_log> {
 | 
			
		||||
  static void to_json(json &data, const repertory::info_log &value) {
 | 
			
		||||
    data["function_name"] = value.function_name;
 | 
			
		||||
    data["msg"] = value.msg;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static void from_json(const json &data, repertory::info_log &value) {
 | 
			
		||||
    data.at("function_name").get_to<std::string>(value.function_name);
 | 
			
		||||
    data.at("msg").get_to<std::string>(value.msg);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
NLOHMANN_JSON_NAMESPACE_END
 | 
			
		||||
 | 
			
		||||
#endif // REPERTORY_INCLUDE_EVENTS_TYPES_INFO_LOG_HPP_
 | 
			
		||||
							
								
								
									
										68
									
								
								repertory/librepertory/include/events/types/trace_log.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								repertory/librepertory/include/events/types/trace_log.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
/*
 | 
			
		||||
  Copyright <2018-2025> <scott.e.graves@protonmail.com>
 | 
			
		||||
 | 
			
		||||
  Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
  of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
  in the Software without restriction, including without limitation the rights
 | 
			
		||||
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
  copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
  furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
  The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
  copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
  SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
#ifndef REPERTORY_INCLUDE_EVENTS_TYPES_TRACE_LOG_HPP_
 | 
			
		||||
#define REPERTORY_INCLUDE_EVENTS_TYPES_TRACE_LOG_HPP_
 | 
			
		||||
 | 
			
		||||
#include "events/i_event.hpp"
 | 
			
		||||
#include "types/repertory.hpp"
 | 
			
		||||
 | 
			
		||||
namespace repertory {
 | 
			
		||||
struct trace_log final : public i_event {
 | 
			
		||||
  trace_log() = default;
 | 
			
		||||
  trace_log(std::string_view function_name_, std::string msg_)
 | 
			
		||||
      : function_name(std::string(function_name_)), msg(std::move(msg_)) {}
 | 
			
		||||
 | 
			
		||||
  static constexpr const event_level level{event_level::trace};
 | 
			
		||||
  static constexpr const std::string_view name{"trace_log"};
 | 
			
		||||
 | 
			
		||||
  std::string function_name;
 | 
			
		||||
  std::string msg;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_event_level() const -> event_level override {
 | 
			
		||||
    return level;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_name() const -> std::string_view override {
 | 
			
		||||
    return name;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_single_line() const -> std::string override {
 | 
			
		||||
    return fmt::format("{}|func|{}|msg|{}", name, function_name, msg);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
} // namespace repertory
 | 
			
		||||
 | 
			
		||||
NLOHMANN_JSON_NAMESPACE_BEGIN
 | 
			
		||||
template <> struct adl_serializer<repertory::trace_log> {
 | 
			
		||||
  static void to_json(json &data, const repertory::trace_log &value) {
 | 
			
		||||
    data["function_name"] = value.function_name;
 | 
			
		||||
    data["msg"] = value.msg;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static void from_json(const json &data, repertory::trace_log &value) {
 | 
			
		||||
    data.at("function_name").get_to<std::string>(value.function_name);
 | 
			
		||||
    data.at("msg").get_to<std::string>(value.msg);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
NLOHMANN_JSON_NAMESPACE_END
 | 
			
		||||
 | 
			
		||||
#endif // REPERTORY_INCLUDE_EVENTS_TYPES_TRACE_LOG_HPP_
 | 
			
		||||
							
								
								
									
										68
									
								
								repertory/librepertory/include/events/types/warn_log.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								repertory/librepertory/include/events/types/warn_log.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
/*
 | 
			
		||||
  Copyright <2018-2025> <scott.e.graves@protonmail.com>
 | 
			
		||||
 | 
			
		||||
  Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
  of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
  in the Software without restriction, including without limitation the rights
 | 
			
		||||
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
  copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
  furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
  The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
  copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
  SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
#ifndef REPERTORY_INCLUDE_EVENTS_TYPES_WARN_LOG_HPP_
 | 
			
		||||
#define REPERTORY_INCLUDE_EVENTS_TYPES_WARN_LOG_HPP_
 | 
			
		||||
 | 
			
		||||
#include "events/i_event.hpp"
 | 
			
		||||
#include "types/repertory.hpp"
 | 
			
		||||
 | 
			
		||||
namespace repertory {
 | 
			
		||||
struct warn_log final : public i_event {
 | 
			
		||||
  warn_log() = default;
 | 
			
		||||
  warn_log(std::string_view function_name_, std::string msg_)
 | 
			
		||||
      : function_name(std::string(function_name_)), msg(std::move(msg_)) {}
 | 
			
		||||
 | 
			
		||||
  static constexpr const event_level level{event_level::warn};
 | 
			
		||||
  static constexpr const std::string_view name{"warn_log"};
 | 
			
		||||
 | 
			
		||||
  std::string function_name;
 | 
			
		||||
  std::string msg;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_event_level() const -> event_level override {
 | 
			
		||||
    return level;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_name() const -> std::string_view override {
 | 
			
		||||
    return name;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_single_line() const -> std::string override {
 | 
			
		||||
    return fmt::format("{}|func|{}|msg|{}", name, function_name, msg);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
} // namespace repertory
 | 
			
		||||
 | 
			
		||||
NLOHMANN_JSON_NAMESPACE_BEGIN
 | 
			
		||||
template <> struct adl_serializer<repertory::warn_log> {
 | 
			
		||||
  static void to_json(json &data, const repertory::warn_log &value) {
 | 
			
		||||
    data["function_name"] = value.function_name;
 | 
			
		||||
    data["msg"] = value.msg;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static void from_json(const json &data, repertory::warn_log &value) {
 | 
			
		||||
    data.at("function_name").get_to<std::string>(value.function_name);
 | 
			
		||||
    data.at("msg").get_to<std::string>(value.msg);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
NLOHMANN_JSON_NAMESPACE_END
 | 
			
		||||
 | 
			
		||||
#endif // REPERTORY_INCLUDE_EVENTS_TYPES_WARN_LOG_HPP_
 | 
			
		||||
@@ -22,6 +22,13 @@
 | 
			
		||||
#ifndef REPERTORY_INCLUDE_PLATFORM_PLATFORM_HPP_
 | 
			
		||||
#define REPERTORY_INCLUDE_PLATFORM_PLATFORM_HPP_
 | 
			
		||||
 | 
			
		||||
#include "types/repertory.hpp"
 | 
			
		||||
 | 
			
		||||
namespace repertory {
 | 
			
		||||
[[nodiscard]] auto create_lock_id(provider_type prov,
 | 
			
		||||
                                  std::string_view unique_id)->std::string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
#include "platform/win32_platform.hpp"
 | 
			
		||||
#include "utils/windows.hpp"
 | 
			
		||||
 
 | 
			
		||||
@@ -30,38 +30,45 @@ class i_provider;
 | 
			
		||||
 | 
			
		||||
class lock_data final {
 | 
			
		||||
public:
 | 
			
		||||
  explicit lock_data(const provider_type &pt, std::string unique_id /*= ""*/);
 | 
			
		||||
  lock_data(provider_type prov, std::string_view unique_id);
 | 
			
		||||
 | 
			
		||||
  lock_data();
 | 
			
		||||
  lock_data(const lock_data &) = delete;
 | 
			
		||||
  lock_data(lock_data &&) = delete;
 | 
			
		||||
 | 
			
		||||
  auto operator=(const lock_data &) -> lock_data & = delete;
 | 
			
		||||
  auto operator=(lock_data &&) -> lock_data & = delete;
 | 
			
		||||
 | 
			
		||||
  ~lock_data();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  const provider_type pt_;
 | 
			
		||||
  const std::string unique_id_;
 | 
			
		||||
  const std::string mutex_id_;
 | 
			
		||||
  int lock_fd_;
 | 
			
		||||
  int lock_status_ = EWOULDBLOCK;
 | 
			
		||||
  std::string mutex_id_;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  int handle_{};
 | 
			
		||||
  int lock_status_{EWOULDBLOCK};
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  [[nodiscard]] static auto get_state_directory() -> std::string;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] static auto get_lock_data_file() -> std::string;
 | 
			
		||||
  [[nodiscard]] auto get_lock_data_file() const -> std::string;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_lock_file() -> std::string;
 | 
			
		||||
  [[nodiscard]] auto get_lock_file() const -> std::string;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  [[nodiscard]] static auto
 | 
			
		||||
  wait_for_lock(int fd, std::uint8_t retry_count = 30u) -> int;
 | 
			
		||||
  [[nodiscard]] static auto wait_for_lock(int handle,
 | 
			
		||||
                                          std::uint8_t retry_count = 30U)
 | 
			
		||||
      -> int;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  [[nodiscard]] auto get_mount_state(json &mount_state) -> bool;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto grab_lock(std::uint8_t retry_count = 30u) -> lock_result;
 | 
			
		||||
  [[nodiscard]] auto grab_lock(std::uint8_t retry_count = 30U) -> lock_result;
 | 
			
		||||
 | 
			
		||||
  void release();
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto set_mount_state(bool active,
 | 
			
		||||
                                     const std::string &mount_location,
 | 
			
		||||
                                     int pid) -> bool;
 | 
			
		||||
                                     std::string_view mount_location, int pid)
 | 
			
		||||
      -> bool;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] auto create_meta_attributes(
 | 
			
		||||
@@ -76,5 +83,5 @@ public:
 | 
			
		||||
                                         const api_file &file) -> api_error;
 | 
			
		||||
} // namespace repertory
 | 
			
		||||
 | 
			
		||||
#endif // _WIN32
 | 
			
		||||
#endif // !defined(_WIN32)
 | 
			
		||||
#endif // REPERTORY_INCLUDE_PLATFORM_UNIXPLATFORM_HPP_
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,6 @@
 | 
			
		||||
#define REPERTORY_INCLUDE_PLATFORM_WINPLATFORM_HPP_
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
 | 
			
		||||
#include "app_config.hpp"
 | 
			
		||||
#include "types/repertory.hpp"
 | 
			
		||||
 | 
			
		||||
namespace repertory {
 | 
			
		||||
@@ -31,43 +30,32 @@ class i_provider;
 | 
			
		||||
 | 
			
		||||
class lock_data final {
 | 
			
		||||
public:
 | 
			
		||||
  explicit lock_data(const provider_type &pt, std::string unique_id /*= ""*/)
 | 
			
		||||
      : pt_(pt),
 | 
			
		||||
        unique_id_(std::move(unique_id)),
 | 
			
		||||
        mutex_id_("repertory_" + app_config::get_provider_name(pt) + "_" +
 | 
			
		||||
                  unique_id_),
 | 
			
		||||
        mutex_handle_(::CreateMutex(nullptr, FALSE, &mutex_id_[0u])) {}
 | 
			
		||||
  explicit lock_data(provider_type prov, std::string unique_id);
 | 
			
		||||
  lock_data(const lock_data &) = delete;
 | 
			
		||||
  lock_data(lock_data &&) = delete;
 | 
			
		||||
 | 
			
		||||
  lock_data()
 | 
			
		||||
      : pt_(provider_type::sia),
 | 
			
		||||
        unique_id_(""),
 | 
			
		||||
        mutex_id_(""),
 | 
			
		||||
        mutex_handle_(INVALID_HANDLE_VALUE) {}
 | 
			
		||||
  ~lock_data();
 | 
			
		||||
 | 
			
		||||
  ~lock_data() { release(); }
 | 
			
		||||
  auto operator=(const lock_data &) -> lock_data & = delete;
 | 
			
		||||
  auto operator=(lock_data &&) -> lock_data & = delete;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  const provider_type pt_;
 | 
			
		||||
  const std::string unique_id_;
 | 
			
		||||
  const std::string mutex_id_;
 | 
			
		||||
  HANDLE mutex_handle_;
 | 
			
		||||
  DWORD mutex_state_ = WAIT_FAILED;
 | 
			
		||||
  std::string mutex_id_;
 | 
			
		||||
  HANDLE mutex_handle_{INVALID_HANDLE_VALUE};
 | 
			
		||||
  DWORD mutex_state_{WAIT_FAILED};
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_current_mount_state(json &mount_state) -> bool;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  [[nodiscard]] auto get_mount_state(const provider_type &pt,
 | 
			
		||||
                                     json &mount_state) -> bool;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_mount_state(json &mount_state) -> bool;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_unique_id() const -> std::string { return unique_id_; }
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto grab_lock(std::uint8_t retry_count = 30) -> lock_result;
 | 
			
		||||
  [[nodiscard]] auto grab_lock(std::uint8_t retry_count = 30U) -> lock_result;
 | 
			
		||||
 | 
			
		||||
  void release();
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto set_mount_state(bool active,
 | 
			
		||||
                                     const std::string &mount_location,
 | 
			
		||||
                                     const std::int64_t &pid) -> bool;
 | 
			
		||||
                                     std::string_view mount_location,
 | 
			
		||||
                                     std::int64_t pid) -> bool;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] auto create_meta_attributes(
 | 
			
		||||
 
 | 
			
		||||
@@ -49,16 +49,28 @@ private:
 | 
			
		||||
  sia_config sia_config_;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  [[nodiscard]] auto create_directory_key(const std::string &api_path) const
 | 
			
		||||
      -> repertory::api_error;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto ensure_directory_exists(const std::string &api_path) const
 | 
			
		||||
      -> api_error;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_object_info(const std::string &api_path,
 | 
			
		||||
                                     json &object_info) const -> api_error;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_object_list(const std::string &api_path,
 | 
			
		||||
                                     nlohmann::json &object_list) const -> bool;
 | 
			
		||||
  [[nodiscard]] auto
 | 
			
		||||
  get_object_list(const std::string &api_path, nlohmann::json &object_list,
 | 
			
		||||
                  std::optional<std::string> marker = std::nullopt) const
 | 
			
		||||
      -> bool;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_sia_config() const -> const auto & {
 | 
			
		||||
    return sia_config_;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void iterate_objects(
 | 
			
		||||
      const std::string &api_path, const json &object_list,
 | 
			
		||||
      std::function<void(const std::string &, bool, json)> handle_entry) const;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
  [[nodiscard]] auto create_directory_impl(const std::string &api_path,
 | 
			
		||||
                                           api_meta_map &meta)
 | 
			
		||||
 
 | 
			
		||||
@@ -48,9 +48,9 @@ public:
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_pinned_files() -> rpc_response;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto pin_file(const std::string &api_file) -> rpc_response;
 | 
			
		||||
  [[nodiscard]] auto pin_file(const std::string &api_path) -> rpc_response;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto pinned_status(const std::string &api_file) -> rpc_response;
 | 
			
		||||
  [[nodiscard]] auto pinned_status(const std::string &api_path) -> rpc_response;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto set_config_value_by_name(const std::string &name,
 | 
			
		||||
                                              const std::string &value)
 | 
			
		||||
@@ -58,7 +58,7 @@ public:
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto unmount() -> rpc_response;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto unpin_file(const std::string &api_file) -> rpc_response;
 | 
			
		||||
  [[nodiscard]] auto unpin_file(const std::string &api_path) -> rpc_response;
 | 
			
		||||
};
 | 
			
		||||
} // namespace repertory
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										82
									
								
								repertory/librepertory/include/rpc/common.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								repertory/librepertory/include/rpc/common.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
			
		||||
/*
 | 
			
		||||
  Copyright <2018-2025> <scott.e.graves@protonmail.com>
 | 
			
		||||
 | 
			
		||||
  Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
  of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
  in the Software without restriction, including without limitation the rights
 | 
			
		||||
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
  copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
  furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
  The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
  copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
  SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
#ifndef REPERTORY_INCLUDE_RPC_COMMON_HPP_
 | 
			
		||||
#define REPERTORY_INCLUDE_RPC_COMMON_HPP_
 | 
			
		||||
 | 
			
		||||
#include "utils/base64.hpp"
 | 
			
		||||
#include "utils/error_utils.hpp"
 | 
			
		||||
#include "utils/string.hpp"
 | 
			
		||||
 | 
			
		||||
namespace repertory::rpc {
 | 
			
		||||
[[nodiscard]] auto check_authorization(const auto &cfg,
 | 
			
		||||
                                       const httplib::Request &req) -> bool {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  if (cfg.get_api_password().empty() || cfg.get_api_user().empty()) {
 | 
			
		||||
    utils::error::raise_error(function_name,
 | 
			
		||||
                              "authorization user or password is not set");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto authorization = req.get_header_value("Authorization");
 | 
			
		||||
  if (authorization.empty()) {
 | 
			
		||||
    utils::error::raise_error(function_name,
 | 
			
		||||
                              "'Authorization' header is not set");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto auth_parts = utils::string::split(authorization, ' ', true);
 | 
			
		||||
  if (auth_parts.empty()) {
 | 
			
		||||
    utils::error::raise_error(function_name, "'Authorization' header is empty");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto auth_type = auth_parts[0U];
 | 
			
		||||
  if (auth_type != "Basic") {
 | 
			
		||||
    utils::error::raise_error(function_name,
 | 
			
		||||
                              "authorization type is not 'Basic'");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto data = macaron::Base64::Decode(authorization.substr(6U));
 | 
			
		||||
  auto auth_str = std::string(data.begin(), data.end());
 | 
			
		||||
 | 
			
		||||
  auto auth = utils::string::split(auth_str, ':', false);
 | 
			
		||||
  if (auth.size() < 2U) {
 | 
			
		||||
    utils::error::raise_error(function_name, "authorization data is not valid");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto user = auth.at(0U);
 | 
			
		||||
  auth.erase(auth.begin());
 | 
			
		||||
 | 
			
		||||
  auto pwd = utils::string::join(auth, ':');
 | 
			
		||||
  if ((user != cfg.get_api_user()) || (pwd != cfg.get_api_password())) {
 | 
			
		||||
    utils::error::raise_error(function_name, "authorization failed");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
} // namespace repertory::rpc
 | 
			
		||||
 | 
			
		||||
#endif // REPERTORY_INCLUDE_RPC_COMMON_HPP_
 | 
			
		||||
@@ -40,8 +40,6 @@ private:
 | 
			
		||||
  std::mutex start_stop_mutex_;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  [[nodiscard]] auto check_authorization(const httplib::Request &req) -> bool;
 | 
			
		||||
 | 
			
		||||
  void handle_get_config(const httplib::Request &req, httplib::Response &res);
 | 
			
		||||
 | 
			
		||||
  void handle_get_config_value_by_name(const httplib::Request &req,
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@
 | 
			
		||||
#define REPERTORY_INCLUDE_TYPES_REPERTORY_HPP_
 | 
			
		||||
 | 
			
		||||
namespace repertory {
 | 
			
		||||
constexpr const auto default_api_auth_size{48U};
 | 
			
		||||
constexpr const auto default_api_password_size{48U};
 | 
			
		||||
constexpr const auto default_download_timeout_secs{30U};
 | 
			
		||||
constexpr const auto default_eviction_delay_mins{1U};
 | 
			
		||||
constexpr const auto default_high_freq_interval_secs{std::uint16_t{30U}};
 | 
			
		||||
@@ -38,6 +38,7 @@ constexpr const auto default_retry_read_count{6U};
 | 
			
		||||
constexpr const auto default_ring_buffer_file_size{512U};
 | 
			
		||||
constexpr const auto default_task_wait_ms{100U};
 | 
			
		||||
constexpr const auto default_timeout_ms{60000U};
 | 
			
		||||
constexpr const auto default_ui_mgmt_port{std::uint16_t{30000U}};
 | 
			
		||||
constexpr const auto max_ring_buffer_file_size{std::uint16_t(1024U)};
 | 
			
		||||
constexpr const auto max_s3_object_name_length{1024U};
 | 
			
		||||
constexpr const auto min_cache_size_bytes{
 | 
			
		||||
@@ -280,6 +281,8 @@ enum class exit_code : std::int32_t {
 | 
			
		||||
  pin_failed = -16,
 | 
			
		||||
  unpin_failed = -17,
 | 
			
		||||
  init_failed = -18,
 | 
			
		||||
  ui_mount_failed = -19,
 | 
			
		||||
  exception = -20,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum http_error_codes : std::int32_t {
 | 
			
		||||
@@ -304,6 +307,18 @@ enum class provider_type : std::size_t {
 | 
			
		||||
  unknown,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] auto
 | 
			
		||||
provider_type_from_string(std::string_view type,
 | 
			
		||||
                          provider_type default_type = provider_type::unknown)
 | 
			
		||||
    -> provider_type;
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] auto provider_type_to_string(provider_type type) -> std::string;
 | 
			
		||||
 | 
			
		||||
void clean_json_config(provider_type prov, nlohmann::json &data);
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] auto clean_json_value(std::string_view name,
 | 
			
		||||
                                    std::string_view data) -> std::string;
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
struct open_file_data final {
 | 
			
		||||
  PVOID directory_buffer{nullptr};
 | 
			
		||||
@@ -452,7 +467,6 @@ using meta_provider_callback = std::function<void(directory_item &)>;
 | 
			
		||||
 | 
			
		||||
inline constexpr const auto JSON_ACCESS_KEY{"AccessKey"};
 | 
			
		||||
inline constexpr const auto JSON_AGENT_STRING{"AgentString"};
 | 
			
		||||
inline constexpr const auto JSON_API_AUTH{"ApiAuth"};
 | 
			
		||||
inline constexpr const auto JSON_API_PARENT{"ApiParent"};
 | 
			
		||||
inline constexpr const auto JSON_API_PASSWORD{"ApiPassword"};
 | 
			
		||||
inline constexpr const auto JSON_API_PATH{"ApiPath"};
 | 
			
		||||
@@ -487,6 +501,7 @@ inline constexpr const auto JSON_MAX_UPLOAD_COUNT{"MaxUploadCount"};
 | 
			
		||||
inline constexpr const auto JSON_MED_FREQ_INTERVAL_SECS{
 | 
			
		||||
    "MedFreqIntervalSeconds"};
 | 
			
		||||
inline constexpr const auto JSON_META{"Meta"};
 | 
			
		||||
inline constexpr const auto JSON_MOUNT_LOCATIONS{"MountLocations"};
 | 
			
		||||
inline constexpr const auto JSON_ONLINE_CHECK_RETRY_SECS{
 | 
			
		||||
    "OnlineCheckRetrySeconds"};
 | 
			
		||||
inline constexpr const auto JSON_PATH{"Path"};
 | 
			
		||||
 
 | 
			
		||||
@@ -49,6 +49,8 @@ static const option password_option = {"-pw", "--password"};
 | 
			
		||||
static const option remote_mount_option = {"-rm", "--remote_mount"};
 | 
			
		||||
static const option set_option = {"-set", "--set"};
 | 
			
		||||
static const option status_option = {"-status", "--status"};
 | 
			
		||||
static const option ui_option = {"-ui", "--ui"};
 | 
			
		||||
static const option ui_port_option = {"-up", "--ui_port"};
 | 
			
		||||
static const option unmount_option = {"-unmount", "--unmount"};
 | 
			
		||||
static const option unpin_file_option = {"-uf", "--unpin_file"};
 | 
			
		||||
static const option user_option = {"-us", "--user"};
 | 
			
		||||
@@ -75,6 +77,8 @@ static const std::vector<option> option_list = {
 | 
			
		||||
    remote_mount_option,
 | 
			
		||||
    set_option,
 | 
			
		||||
    status_option,
 | 
			
		||||
    ui_option,
 | 
			
		||||
    ui_port_option,
 | 
			
		||||
    unmount_option,
 | 
			
		||||
    unpin_file_option,
 | 
			
		||||
    user_option,
 | 
			
		||||
@@ -87,26 +91,27 @@ void get_api_authentication_data(std::string &user, std::string &password,
 | 
			
		||||
                                 std::uint16_t &port, const provider_type &prov,
 | 
			
		||||
                                 const std::string &data_directory);
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] auto
 | 
			
		||||
get_provider_type_from_args(std::vector<const char *> args) -> provider_type;
 | 
			
		||||
[[nodiscard]] auto get_provider_type_from_args(std::vector<const char *> args)
 | 
			
		||||
    -> provider_type;
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] auto has_option(std::vector<const char *> args,
 | 
			
		||||
                              const std::string &option_name) -> bool;
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] auto has_option(std::vector<const char *> args,
 | 
			
		||||
                              const option &opt) -> bool;
 | 
			
		||||
[[nodiscard]] auto has_option(std::vector<const char *> args, const option &opt)
 | 
			
		||||
    -> bool;
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] auto parse_option(std::vector<const char *> args,
 | 
			
		||||
                                const std::string &option_name,
 | 
			
		||||
                                std::uint8_t count) -> std::vector<std::string>;
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] auto parse_string_option(std::vector<const char *> args,
 | 
			
		||||
                                       const option &opt,
 | 
			
		||||
                                       std::string &value) -> exit_code;
 | 
			
		||||
                                       const option &opt, std::string &value)
 | 
			
		||||
    -> exit_code;
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] auto
 | 
			
		||||
parse_drive_options(std::vector<const char *> args, provider_type &prov,
 | 
			
		||||
                    std::string &data_directory) -> std::vector<std::string>;
 | 
			
		||||
[[nodiscard]] auto parse_drive_options(std::vector<const char *> args,
 | 
			
		||||
                                       provider_type &prov,
 | 
			
		||||
                                       std::string &data_directory)
 | 
			
		||||
    -> std::vector<std::string>;
 | 
			
		||||
} // namespace repertory::utils::cli
 | 
			
		||||
 | 
			
		||||
#endif // REPERTORY_INCLUDE_UTILS_CLI_UTILS_HPP_
 | 
			
		||||
 
 | 
			
		||||
@@ -66,8 +66,8 @@ void app_config::set_stop_requested() { stop_requested.store(true); }
 | 
			
		||||
app_config::app_config(const provider_type &prov,
 | 
			
		||||
                       std::string_view data_directory)
 | 
			
		||||
    : prov_(prov),
 | 
			
		||||
      api_auth_(utils::generate_random_string(default_api_auth_size)),
 | 
			
		||||
      api_port_(default_rpc_port(prov)),
 | 
			
		||||
      api_password_(utils::generate_random_string(default_api_password_size)),
 | 
			
		||||
      api_port_(default_rpc_port()),
 | 
			
		||||
      api_user_(std::string{REPERTORY}),
 | 
			
		||||
      config_changed_(false),
 | 
			
		||||
      download_timeout_secs_(default_download_timeout_secs),
 | 
			
		||||
@@ -124,7 +124,7 @@ app_config::app_config(const provider_type &prov,
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  value_get_lookup_ = {
 | 
			
		||||
      {JSON_API_AUTH, [this]() { return get_api_auth(); }},
 | 
			
		||||
      {JSON_API_PASSWORD, [this]() { return get_api_password(); }},
 | 
			
		||||
      {JSON_API_PORT, [this]() { return std::to_string(get_api_port()); }},
 | 
			
		||||
      {JSON_API_USER, [this]() { return get_api_user(); }},
 | 
			
		||||
      {JSON_DATABASE_TYPE,
 | 
			
		||||
@@ -253,10 +253,10 @@ app_config::app_config(const provider_type &prov,
 | 
			
		||||
 | 
			
		||||
  value_set_lookup_ = {
 | 
			
		||||
      {
 | 
			
		||||
          JSON_API_AUTH,
 | 
			
		||||
          JSON_API_PASSWORD,
 | 
			
		||||
          [this](const std::string &value) {
 | 
			
		||||
            set_api_auth(value);
 | 
			
		||||
            return get_api_auth();
 | 
			
		||||
            set_api_password(value);
 | 
			
		||||
            return get_api_password();
 | 
			
		||||
          },
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
@@ -699,36 +699,37 @@ auto app_config::default_api_port(const provider_type &prov) -> std::uint16_t {
 | 
			
		||||
  return PROVIDER_API_PORTS.at(static_cast<std::size_t>(prov));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto app_config::default_data_directory(const provider_type &prov)
 | 
			
		||||
    -> std::string {
 | 
			
		||||
auto app_config::get_root_data_directory() -> std::string {
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
  auto data_directory =
 | 
			
		||||
      utils::path::combine(utils::get_local_app_data_directory(),
 | 
			
		||||
                           {
 | 
			
		||||
  auto data_directory = utils::path::combine(
 | 
			
		||||
      utils::get_local_app_data_directory(), {
 | 
			
		||||
                                                 REPERTORY_DATA_NAME,
 | 
			
		||||
                               app_config::get_provider_name(prov),
 | 
			
		||||
                                             });
 | 
			
		||||
#else // !defined(_WIN32)
 | 
			
		||||
#if defined(__APPLE__)
 | 
			
		||||
  auto data_directory =
 | 
			
		||||
      utils::path::combine("~", {
 | 
			
		||||
  auto data_directory = utils::path::combine("~", {
 | 
			
		||||
                                                      "Library",
 | 
			
		||||
                                                      "Application Support",
 | 
			
		||||
                                                      REPERTORY_DATA_NAME,
 | 
			
		||||
                                    app_config::get_provider_name(prov),
 | 
			
		||||
                                                  });
 | 
			
		||||
#else  // !defined(__APPLE__)
 | 
			
		||||
  auto data_directory =
 | 
			
		||||
      utils::path::combine("~", {
 | 
			
		||||
  auto data_directory = utils::path::combine("~", {
 | 
			
		||||
                                                      ".local",
 | 
			
		||||
                                                      REPERTORY_DATA_NAME,
 | 
			
		||||
                                    app_config::get_provider_name(prov),
 | 
			
		||||
                                                  });
 | 
			
		||||
#endif // defined(__APPLE__)
 | 
			
		||||
#endif // defined(_WIN32)
 | 
			
		||||
  return data_directory;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto app_config::default_data_directory(const provider_type &prov)
 | 
			
		||||
    -> std::string {
 | 
			
		||||
  return utils::path::combine(app_config::get_root_data_directory(),
 | 
			
		||||
                              {
 | 
			
		||||
                                  app_config::get_provider_name(prov),
 | 
			
		||||
                              });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto app_config::default_remote_api_port(const provider_type &prov)
 | 
			
		||||
    -> std::uint16_t {
 | 
			
		||||
  static const std::array<std::uint16_t,
 | 
			
		||||
@@ -741,19 +742,12 @@ auto app_config::default_remote_api_port(const provider_type &prov)
 | 
			
		||||
      };
 | 
			
		||||
  return PROVIDER_REMOTE_PORTS.at(static_cast<std::size_t>(prov));
 | 
			
		||||
}
 | 
			
		||||
auto app_config::default_rpc_port(const provider_type &prov) -> std::uint16_t {
 | 
			
		||||
  static const std::array<std::uint16_t,
 | 
			
		||||
                          static_cast<std::size_t>(provider_type::unknown)>
 | 
			
		||||
      PROVIDER_RPC_PORTS = {
 | 
			
		||||
          10000U,
 | 
			
		||||
          10010U,
 | 
			
		||||
          10100U,
 | 
			
		||||
          10002U,
 | 
			
		||||
      };
 | 
			
		||||
  return PROVIDER_RPC_PORTS.at(static_cast<std::size_t>(prov));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto app_config::get_api_auth() const -> std::string { return api_auth_; }
 | 
			
		||||
auto app_config::default_rpc_port() -> std::uint16_t { return 10000U; }
 | 
			
		||||
 | 
			
		||||
auto app_config::get_api_password() const -> std::string {
 | 
			
		||||
  return api_password_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto app_config::get_api_port() const -> std::uint16_t { return api_port_; }
 | 
			
		||||
 | 
			
		||||
@@ -814,7 +808,7 @@ auto app_config::get_host_config() const -> host_config { return host_config_; }
 | 
			
		||||
 | 
			
		||||
auto app_config::get_json() const -> json {
 | 
			
		||||
  json ret = {
 | 
			
		||||
      {JSON_API_AUTH, api_auth_},
 | 
			
		||||
      {JSON_API_PASSWORD, api_password_},
 | 
			
		||||
      {JSON_API_PORT, api_port_},
 | 
			
		||||
      {JSON_API_USER, api_user_},
 | 
			
		||||
      {JSON_DOWNLOAD_TIMEOUT_SECS, download_timeout_secs_},
 | 
			
		||||
@@ -939,24 +933,18 @@ auto app_config::get_preferred_download_type() const -> download_type {
 | 
			
		||||
auto app_config::get_provider_display_name(const provider_type &prov)
 | 
			
		||||
    -> std::string {
 | 
			
		||||
  static const std::array<std::string,
 | 
			
		||||
                          static_cast<std::size_t>(provider_type::unknown)>
 | 
			
		||||
                          static_cast<std::size_t>(provider_type::unknown) + 1U>
 | 
			
		||||
      PROVIDER_DISPLAY_NAMES = {
 | 
			
		||||
          "Sia",
 | 
			
		||||
          "Remote",
 | 
			
		||||
          "S3",
 | 
			
		||||
          "Encrypt",
 | 
			
		||||
          "Sia", "Remote", "S3", "Encrypt", "Unknown",
 | 
			
		||||
      };
 | 
			
		||||
  return PROVIDER_DISPLAY_NAMES.at(static_cast<std::size_t>(prov));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto app_config::get_provider_name(const provider_type &prov) -> std::string {
 | 
			
		||||
  static const std::array<std::string,
 | 
			
		||||
                          static_cast<std::size_t>(provider_type::unknown)>
 | 
			
		||||
                          static_cast<std::size_t>(provider_type::unknown) + 1U>
 | 
			
		||||
      PROVIDER_NAMES = {
 | 
			
		||||
          "sia",
 | 
			
		||||
          "remote",
 | 
			
		||||
          "s3",
 | 
			
		||||
          "encrypt",
 | 
			
		||||
          "sia", "remote", "s3", "encrypt", "unknown",
 | 
			
		||||
      };
 | 
			
		||||
  return PROVIDER_NAMES.at(static_cast<std::size_t>(prov));
 | 
			
		||||
}
 | 
			
		||||
@@ -1035,7 +1023,7 @@ auto app_config::load() -> bool {
 | 
			
		||||
    auto found{true};
 | 
			
		||||
    auto json_document = json::parse(json_text);
 | 
			
		||||
 | 
			
		||||
    get_value(json_document, JSON_API_AUTH, api_auth_, found);
 | 
			
		||||
    get_value(json_document, JSON_API_PASSWORD, api_password_, found);
 | 
			
		||||
    get_value(json_document, JSON_API_PORT, api_port_, found);
 | 
			
		||||
    get_value(json_document, JSON_API_USER, api_user_, found);
 | 
			
		||||
    get_value(json_document, JSON_DATABASE_TYPE, db_type_, found);
 | 
			
		||||
@@ -1092,6 +1080,13 @@ auto app_config::load() -> bool {
 | 
			
		||||
          set_value(max_cache_size_bytes_, default_max_cache_size_bytes);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (version_ == 2U) {
 | 
			
		||||
        if (json_document.contains("ApiAuth")) {
 | 
			
		||||
          api_password_ = json_document.at("ApiAuth").get<std::string>();
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      found = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1130,8 +1125,8 @@ void app_config::save() {
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void app_config::set_api_auth(const std::string &value) {
 | 
			
		||||
  set_value(api_auth_, value);
 | 
			
		||||
void app_config::set_api_password(const std::string &value) {
 | 
			
		||||
  set_value(api_password_, value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void app_config::set_api_port(std::uint16_t value) {
 | 
			
		||||
 
 | 
			
		||||
@@ -374,13 +374,13 @@ auto fuse_base::init_impl(struct fuse_conn_info *conn) -> void * {
 | 
			
		||||
  if (not utils::file::change_to_process_directory()) {
 | 
			
		||||
    utils::error::raise_error(function_name,
 | 
			
		||||
                              "failed to change to process directory");
 | 
			
		||||
    event_system::instance().raise<unmount_requested>();
 | 
			
		||||
    event_system::instance().raise<unmount_requested>(function_name);
 | 
			
		||||
    return this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (not console_enabled_ && not repertory::project_initialize()) {
 | 
			
		||||
    utils::error::raise_error(function_name, "failed to initialize repertory");
 | 
			
		||||
    event_system::instance().raise<unmount_requested>();
 | 
			
		||||
    event_system::instance().raise<unmount_requested>(function_name);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return this;
 | 
			
		||||
 
 | 
			
		||||
@@ -85,8 +85,8 @@ auto fuse_drive::chown_impl(std::string api_path, uid_t uid, gid_t gid,
 | 
			
		||||
                            struct fuse_file_info * /*file_info*/)
 | 
			
		||||
    -> api_error {
 | 
			
		||||
#else
 | 
			
		||||
auto fuse_drive::chown_impl(std::string api_path, uid_t uid,
 | 
			
		||||
                            gid_t gid) -> api_error {
 | 
			
		||||
auto fuse_drive::chown_impl(std::string api_path, uid_t uid, gid_t gid)
 | 
			
		||||
    -> api_error {
 | 
			
		||||
#endif
 | 
			
		||||
  return check_and_perform(
 | 
			
		||||
      api_path, X_OK, [&](api_meta_map &meta) -> api_error {
 | 
			
		||||
@@ -464,7 +464,7 @@ auto fuse_drive::get_file_size(const std::string &api_path) const
 | 
			
		||||
 | 
			
		||||
  std::uint64_t file_size{};
 | 
			
		||||
  auto res = provider_.get_file_size(api_path, file_size);
 | 
			
		||||
  if (res == api_error::success) {
 | 
			
		||||
  if (res != api_error::success) {
 | 
			
		||||
    utils::error::raise_api_path_error(function_name, api_path, res,
 | 
			
		||||
                                       "failed to get file size from provider");
 | 
			
		||||
  }
 | 
			
		||||
@@ -494,8 +494,8 @@ auto fuse_drive::getattr_impl(std::string api_path, struct stat *unix_st,
 | 
			
		||||
                              struct fuse_file_info * /*file_info*/)
 | 
			
		||||
    -> api_error {
 | 
			
		||||
#else
 | 
			
		||||
auto fuse_drive::getattr_impl(std::string api_path,
 | 
			
		||||
                              struct stat *unix_st) -> api_error {
 | 
			
		||||
auto fuse_drive::getattr_impl(std::string api_path, struct stat *unix_st)
 | 
			
		||||
    -> api_error {
 | 
			
		||||
#endif
 | 
			
		||||
  auto parent = utils::path::get_parent_api_path(api_path);
 | 
			
		||||
 | 
			
		||||
@@ -561,8 +561,8 @@ auto fuse_drive::getxtimes_impl(std::string api_path, struct timespec *bkuptime,
 | 
			
		||||
#endif // __APPLE__
 | 
			
		||||
 | 
			
		||||
#if FUSE_USE_VERSION >= 30
 | 
			
		||||
auto fuse_drive::init_impl(struct fuse_conn_info *conn,
 | 
			
		||||
                           struct fuse_config *cfg) -> void * {
 | 
			
		||||
auto fuse_drive::init_impl(struct fuse_conn_info *conn, struct fuse_config *cfg)
 | 
			
		||||
    -> void * {
 | 
			
		||||
#else
 | 
			
		||||
void *fuse_drive::init_impl(struct fuse_conn_info *conn) {
 | 
			
		||||
#endif
 | 
			
		||||
@@ -804,8 +804,9 @@ auto fuse_drive::release_impl(std::string /*api_path*/,
 | 
			
		||||
  return api_error::success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto fuse_drive::releasedir_impl(
 | 
			
		||||
    std::string /*api_path*/, struct fuse_file_info *file_info) -> api_error {
 | 
			
		||||
auto fuse_drive::releasedir_impl(std::string /*api_path*/,
 | 
			
		||||
                                 struct fuse_file_info *file_info)
 | 
			
		||||
    -> api_error {
 | 
			
		||||
  auto iter = directory_cache_->get_directory(file_info->fh);
 | 
			
		||||
  if (iter == nullptr) {
 | 
			
		||||
    return api_error::invalid_handle;
 | 
			
		||||
@@ -823,8 +824,8 @@ auto fuse_drive::rename_directory(const std::string &from_api_path,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto fuse_drive::rename_file(const std::string &from_api_path,
 | 
			
		||||
                             const std::string &to_api_path,
 | 
			
		||||
                             bool overwrite) -> int {
 | 
			
		||||
                             const std::string &to_api_path, bool overwrite)
 | 
			
		||||
    -> int {
 | 
			
		||||
  auto res = fm_->rename_file(from_api_path, to_api_path, overwrite);
 | 
			
		||||
  errno = std::abs(utils::from_api_error(res));
 | 
			
		||||
  return (res == api_error::success) ? 0 : -1;
 | 
			
		||||
@@ -834,8 +835,8 @@ auto fuse_drive::rename_file(const std::string &from_api_path,
 | 
			
		||||
auto fuse_drive::rename_impl(std::string from_api_path, std::string to_api_path,
 | 
			
		||||
                             unsigned int /*flags*/) -> api_error {
 | 
			
		||||
#else
 | 
			
		||||
auto fuse_drive::rename_impl(std::string from_api_path,
 | 
			
		||||
                             std::string to_api_path) -> api_error {
 | 
			
		||||
auto fuse_drive::rename_impl(std::string from_api_path, std::string to_api_path)
 | 
			
		||||
    -> api_error {
 | 
			
		||||
#endif
 | 
			
		||||
  auto res = check_parent_access(to_api_path, W_OK | X_OK);
 | 
			
		||||
  if (res != api_error::success) {
 | 
			
		||||
@@ -937,15 +938,15 @@ auto fuse_drive::getxattr_impl(std::string api_path, const char *name,
 | 
			
		||||
}
 | 
			
		||||
#else  // __APPLE__
 | 
			
		||||
auto fuse_drive::getxattr_impl(std::string api_path, const char *name,
 | 
			
		||||
                               char *value, size_t size,
 | 
			
		||||
                               int &attribute_size) -> api_error {
 | 
			
		||||
                               char *value, size_t size, int &attribute_size)
 | 
			
		||||
    -> api_error {
 | 
			
		||||
  return getxattr_common(api_path, name, value, size, attribute_size, nullptr);
 | 
			
		||||
}
 | 
			
		||||
#endif // __APPLE__
 | 
			
		||||
 | 
			
		||||
auto fuse_drive::listxattr_impl(std::string api_path, char *buffer, size_t size,
 | 
			
		||||
                                int &required_size,
 | 
			
		||||
                                bool &return_size) -> api_error {
 | 
			
		||||
                                int &required_size, bool &return_size)
 | 
			
		||||
    -> api_error {
 | 
			
		||||
  auto check_size = (size == 0);
 | 
			
		||||
 | 
			
		||||
  auto res = check_parent_access(api_path, X_OK);
 | 
			
		||||
@@ -985,8 +986,8 @@ auto fuse_drive::listxattr_impl(std::string api_path, char *buffer, size_t size,
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto fuse_drive::removexattr_impl(std::string api_path,
 | 
			
		||||
                                  const char *name) -> api_error {
 | 
			
		||||
auto fuse_drive::removexattr_impl(std::string api_path, const char *name)
 | 
			
		||||
    -> api_error {
 | 
			
		||||
  std::string attribute_name;
 | 
			
		||||
#if defined(__APPLE__)
 | 
			
		||||
  auto res = parse_xattr_parameters(name, 0, attribute_name, api_path);
 | 
			
		||||
@@ -1014,8 +1015,8 @@ auto fuse_drive::setxattr_impl(std::string api_path, const char *name,
 | 
			
		||||
                               uint32_t position) -> api_error {
 | 
			
		||||
#else // __APPLE__
 | 
			
		||||
auto fuse_drive::setxattr_impl(std::string api_path, const char *name,
 | 
			
		||||
                               const char *value, size_t size,
 | 
			
		||||
                               int flags) -> api_error {
 | 
			
		||||
                               const char *value, size_t size, int flags)
 | 
			
		||||
    -> api_error {
 | 
			
		||||
#endif
 | 
			
		||||
  std::string attribute_name;
 | 
			
		||||
#if defined(__APPLE__)
 | 
			
		||||
@@ -1086,15 +1087,27 @@ void fuse_drive::set_item_meta(const std::string &api_path,
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  auto res = provider_.set_item_meta(api_path, key, value);
 | 
			
		||||
  if (res != api_error::success) {
 | 
			
		||||
    utils::error::raise_api_path_error(
 | 
			
		||||
        function_name, api_path, res,
 | 
			
		||||
        fmt::format("failed to set item meta|key|{}", key));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void fuse_drive::set_item_meta(const std::string &api_path,
 | 
			
		||||
                               const api_meta_map &meta) {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  auto res = provider_.set_item_meta(api_path, meta);
 | 
			
		||||
  if (res != api_error::success) {
 | 
			
		||||
    utils::error::raise_api_path_error(function_name, api_path, res,
 | 
			
		||||
                                       "key|" + key + "|value|" + value);
 | 
			
		||||
                                       "failed to set item meta");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if defined(__APPLE__)
 | 
			
		||||
auto fuse_drive::setattr_x_impl(std::string api_path,
 | 
			
		||||
                                struct setattr_x *attr) -> api_error {
 | 
			
		||||
auto fuse_drive::setattr_x_impl(std::string api_path, struct setattr_x *attr)
 | 
			
		||||
    -> api_error {
 | 
			
		||||
  bool exists{};
 | 
			
		||||
  auto res = provider_.is_file(api_path, exists);
 | 
			
		||||
  if (res != api_error::success) {
 | 
			
		||||
@@ -1193,8 +1206,9 @@ auto fuse_drive::setattr_x_impl(std::string api_path,
 | 
			
		||||
  return api_error::success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto fuse_drive::setbkuptime_impl(
 | 
			
		||||
    std::string api_path, const struct timespec *bkuptime) -> api_error {
 | 
			
		||||
auto fuse_drive::setbkuptime_impl(std::string api_path,
 | 
			
		||||
                                  const struct timespec *bkuptime)
 | 
			
		||||
    -> api_error {
 | 
			
		||||
  return check_and_perform(
 | 
			
		||||
      api_path, X_OK, [&](api_meta_map &meta) -> api_error {
 | 
			
		||||
        auto nanos = bkuptime->tv_nsec +
 | 
			
		||||
@@ -1230,8 +1244,8 @@ auto fuse_drive::setvolname_impl(const char * /*volname*/) -> api_error {
 | 
			
		||||
  return api_error::success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto fuse_drive::statfs_x_impl(std::string /*api_path*/,
 | 
			
		||||
                               struct statfs *stbuf) -> api_error {
 | 
			
		||||
auto fuse_drive::statfs_x_impl(std::string /*api_path*/, struct statfs *stbuf)
 | 
			
		||||
    -> api_error {
 | 
			
		||||
  if (statfs(&config_.get_cache_directory()[0], stbuf) != 0) {
 | 
			
		||||
    return api_error::os_error;
 | 
			
		||||
  }
 | 
			
		||||
@@ -1256,8 +1270,8 @@ auto fuse_drive::statfs_x_impl(std::string /*api_path*/,
 | 
			
		||||
  return api_error::success;
 | 
			
		||||
}
 | 
			
		||||
#else  // __APPLE__
 | 
			
		||||
auto fuse_drive::statfs_impl(std::string /*api_path*/,
 | 
			
		||||
                             struct statvfs *stbuf) -> api_error {
 | 
			
		||||
auto fuse_drive::statfs_impl(std::string /*api_path*/, struct statvfs *stbuf)
 | 
			
		||||
    -> api_error {
 | 
			
		||||
  if (statvfs(config_.get_cache_directory().data(), stbuf) != 0) {
 | 
			
		||||
    return api_error::os_error;
 | 
			
		||||
  }
 | 
			
		||||
@@ -1341,8 +1355,8 @@ auto fuse_drive::utimens_impl(std::string api_path, const struct timespec tv[2],
 | 
			
		||||
                              struct fuse_file_info * /*file_info*/)
 | 
			
		||||
    -> api_error {
 | 
			
		||||
#else
 | 
			
		||||
auto fuse_drive::utimens_impl(std::string api_path,
 | 
			
		||||
                              const struct timespec tv[2]) -> api_error {
 | 
			
		||||
auto fuse_drive::utimens_impl(std::string api_path, const struct timespec tv[2])
 | 
			
		||||
    -> api_error {
 | 
			
		||||
#endif
 | 
			
		||||
  api_meta_map meta;
 | 
			
		||||
  auto res = provider_.get_item_meta(api_path, meta);
 | 
			
		||||
 
 | 
			
		||||
@@ -259,7 +259,7 @@ auto remote_fuse_drive::init_impl(struct fuse_conn_info *conn) -> void * {
 | 
			
		||||
  if (remote_instance_->fuse_init() != 0) {
 | 
			
		||||
    utils::error::raise_error(function_name,
 | 
			
		||||
                              "failed to connect to remote server");
 | 
			
		||||
    event_system::instance().raise<unmount_requested>();
 | 
			
		||||
    event_system::instance().raise<unmount_requested>(function_name);
 | 
			
		||||
  } else {
 | 
			
		||||
    server_ = std::make_shared<server>(config_);
 | 
			
		||||
    server_->start();
 | 
			
		||||
 
 | 
			
		||||
@@ -28,6 +28,7 @@
 | 
			
		||||
#include "drives/directory_iterator.hpp"
 | 
			
		||||
#include "drives/remote/remote_open_file_table.hpp"
 | 
			
		||||
#include "events/event_system.hpp"
 | 
			
		||||
#include "events/types/debug_log.hpp"
 | 
			
		||||
#include "events/types/remote_server_event.hpp"
 | 
			
		||||
#include "platform/platform.hpp"
 | 
			
		||||
#include "types/remote.hpp"
 | 
			
		||||
@@ -83,17 +84,18 @@ auto remote_server::populate_file_info(const std::string &api_path,
 | 
			
		||||
                                       remote::file_info &file_info)
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  std::string meta_attributes;
 | 
			
		||||
  auto directory = utils::file::directory(construct_path(api_path)).exists();
 | 
			
		||||
 | 
			
		||||
  auto error = drive_.get_item_meta(api_path, META_ATTRIBUTES, meta_attributes);
 | 
			
		||||
  if (error == api_error::success) {
 | 
			
		||||
    if (meta_attributes.empty()) {
 | 
			
		||||
      meta_attributes =
 | 
			
		||||
          utils::file::directory(construct_path(api_path)).exists()
 | 
			
		||||
              ? std::to_string(FILE_ATTRIBUTE_DIRECTORY)
 | 
			
		||||
      meta_attributes = directory ? std::to_string(FILE_ATTRIBUTE_DIRECTORY)
 | 
			
		||||
                                  : std::to_string(FILE_ATTRIBUTE_ARCHIVE);
 | 
			
		||||
      drive_.set_item_meta(api_path, META_ATTRIBUTES, meta_attributes);
 | 
			
		||||
    }
 | 
			
		||||
    const auto attributes = utils::string::to_uint32(meta_attributes);
 | 
			
		||||
    const auto file_size = drive_.get_file_size(api_path);
 | 
			
		||||
 | 
			
		||||
    auto attributes = utils::string::to_uint32(meta_attributes);
 | 
			
		||||
    auto file_size = directory ? 0U : drive_.get_file_size(api_path);
 | 
			
		||||
    populate_file_info(api_path, file_size, attributes, file_info);
 | 
			
		||||
    return STATUS_SUCCESS;
 | 
			
		||||
  }
 | 
			
		||||
@@ -108,7 +110,7 @@ void remote_server::populate_file_info(const std::string &api_path,
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  api_meta_map meta{};
 | 
			
		||||
  const auto res = drive_.get_item_meta(api_path, meta);
 | 
			
		||||
  auto res = drive_.get_item_meta(api_path, meta);
 | 
			
		||||
  if (res != api_error::success) {
 | 
			
		||||
    utils::error::raise_api_path_error(function_name, api_path, res,
 | 
			
		||||
                                       "get item meta failed");
 | 
			
		||||
@@ -192,10 +194,10 @@ auto remote_server::fuse_access(const char *path, const std::int32_t &mask)
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
 | 
			
		||||
  const auto res = access(file_path.c_str(), mask);
 | 
			
		||||
  auto res = access(file_path.c_str(), mask);
 | 
			
		||||
  auto ret = ((res < 0) ? -errno : 0);
 | 
			
		||||
  RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, ret);
 | 
			
		||||
  return ret;
 | 
			
		||||
@@ -205,8 +207,8 @@ auto remote_server::fuse_chflags(const char *path, std::uint32_t flags)
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
 | 
			
		||||
  auto ret = -EACCES;
 | 
			
		||||
  if (drive_.check_parent_access(api_path, X_OK) == api_error::success) {
 | 
			
		||||
@@ -225,8 +227,8 @@ auto remote_server::fuse_chmod(const char *path, const remote::file_mode &mode)
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  const auto res = chmod(file_path.c_str(), mode);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
  auto res = chmod(file_path.c_str(), mode);
 | 
			
		||||
  auto ret = ((res < 0) ? -errno : 0);
 | 
			
		||||
  RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, ret);
 | 
			
		||||
  return ret;
 | 
			
		||||
@@ -237,8 +239,8 @@ auto remote_server::fuse_chown(const char *path, const remote::user_id &uid,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  const auto res = chown(file_path.c_str(), uid, gid);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
  auto res = chown(file_path.c_str(), uid, gid);
 | 
			
		||||
  auto ret = ((res < 0) ? -errno : 0);
 | 
			
		||||
  RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, ret);
 | 
			
		||||
  return ret;
 | 
			
		||||
@@ -250,9 +252,8 @@ auto remote_server::fuse_create(const char *path, const remote::file_mode &mode,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  const auto res =
 | 
			
		||||
      open(file_path.c_str(),
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
  auto res = open(file_path.c_str(),
 | 
			
		||||
                  static_cast<int>(remote::create_os_open_flags(flags)), mode);
 | 
			
		||||
  if (res >= 0) {
 | 
			
		||||
    handle = static_cast<remote::file_handle>(res);
 | 
			
		||||
@@ -322,7 +323,7 @@ auto remote_server::fuse_fgetattr(const char *path, remote::stat &r_stat,
 | 
			
		||||
 | 
			
		||||
  r_stat = {};
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
 | 
			
		||||
  auto res = has_open_info(static_cast<native_handle>(handle), EBADF);
 | 
			
		||||
  if (res == 0) {
 | 
			
		||||
@@ -345,8 +346,8 @@ auto remote_server::fuse_fsetattr_x(const char *path,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
 | 
			
		||||
  auto res = 0;
 | 
			
		||||
  if (SETATTR_WANTS_MODE(&attr)) {
 | 
			
		||||
@@ -440,7 +441,7 @@ auto remote_server::fuse_fsync(const char *path, const std::int32_t &datasync,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
 | 
			
		||||
  auto res = has_open_info(static_cast<native_handle>(handle), EBADF);
 | 
			
		||||
  if (res == 0) {
 | 
			
		||||
@@ -464,7 +465,7 @@ auto remote_server::fuse_ftruncate(const char *path,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
 | 
			
		||||
  auto res = has_open_info(static_cast<native_handle>(handle), EBADF);
 | 
			
		||||
  if (res == 0) {
 | 
			
		||||
@@ -481,9 +482,9 @@ auto remote_server::fuse_getattr(const char *path, remote::stat &r_stat,
 | 
			
		||||
                                 bool &directory) -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  const auto file_path = construct_path(api_path);
 | 
			
		||||
  const auto parent_api_path = utils::path::get_parent_api_path(api_path);
 | 
			
		||||
  auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  auto file_path = construct_path(api_path);
 | 
			
		||||
  auto parent_api_path = utils::path::get_parent_api_path(api_path);
 | 
			
		||||
 | 
			
		||||
  r_stat = {};
 | 
			
		||||
 | 
			
		||||
@@ -560,8 +561,8 @@ auto remote_server::fuse_getxtimes(const char *path,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
 | 
			
		||||
  auto ret = -EACCES;
 | 
			
		||||
  if (drive_.check_parent_access(api_path, X_OK) == api_error::success) {
 | 
			
		||||
@@ -608,8 +609,8 @@ auto remote_server::fuse_mkdir(const char *path, const remote::file_mode &mode)
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  const auto res = mkdir(file_path.c_str(), mode);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
  auto res = mkdir(file_path.c_str(), mode);
 | 
			
		||||
  auto ret = ((res < 0) ? -errno : 0);
 | 
			
		||||
  RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, ret);
 | 
			
		||||
  return ret;
 | 
			
		||||
@@ -620,8 +621,8 @@ auto remote_server::fuse_open(const char *path, const remote::open_flags &flags,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  const auto res = open(file_path.c_str(),
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
  auto res = open(file_path.c_str(),
 | 
			
		||||
                  static_cast<int>(remote::create_os_open_flags(flags)));
 | 
			
		||||
  if (res >= 0) {
 | 
			
		||||
    handle = static_cast<remote::file_handle>(res);
 | 
			
		||||
@@ -641,7 +642,7 @@ auto remote_server::fuse_opendir(const char *path, remote::file_handle &handle)
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
 | 
			
		||||
  auto res = -1;
 | 
			
		||||
  errno = ENOENT;
 | 
			
		||||
@@ -669,7 +670,7 @@ auto remote_server::fuse_read(const char *path, char *buffer,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
  auto &data = *reinterpret_cast<data_buffer *>(buffer);
 | 
			
		||||
 | 
			
		||||
  ssize_t bytes_read{has_open_info(static_cast<native_handle>(handle), EBADF)};
 | 
			
		||||
@@ -691,9 +692,9 @@ auto remote_server::fuse_rename(const char *from, const char *to)
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto from_path = utils::path::combine(mount_location_, {from});
 | 
			
		||||
  const auto to_path = utils::path::combine(mount_location_, {to});
 | 
			
		||||
  const auto res = rename(from_path.c_str(), to_path.c_str());
 | 
			
		||||
  auto from_path = utils::path::combine(mount_location_, {from});
 | 
			
		||||
  auto to_path = utils::path::combine(mount_location_, {to});
 | 
			
		||||
  auto res = rename(from_path.c_str(), to_path.c_str());
 | 
			
		||||
  auto ret = ((res < 0) ? -errno : 0);
 | 
			
		||||
  RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, from + std::string("|") + to,
 | 
			
		||||
                                 ret);
 | 
			
		||||
@@ -706,7 +707,7 @@ auto remote_server::fuse_readdir(const char *path,
 | 
			
		||||
                                 std::string &item_path) -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
  auto res = 0;
 | 
			
		||||
  if (offset > std::numeric_limits<std::size_t>::max()) {
 | 
			
		||||
    errno = ERANGE;
 | 
			
		||||
@@ -733,7 +734,7 @@ auto remote_server::fuse_release(const char *path,
 | 
			
		||||
 | 
			
		||||
  packet::error_type ret = 0;
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
  auto res = has_open_info(static_cast<native_handle>(handle), EBADF);
 | 
			
		||||
  if (res == 0) {
 | 
			
		||||
    res = close(static_cast<native_handle>(handle));
 | 
			
		||||
@@ -750,7 +751,7 @@ auto remote_server::fuse_releasedir(const char *path,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
 | 
			
		||||
  directory_cache_.remove_directory(handle);
 | 
			
		||||
 | 
			
		||||
@@ -771,8 +772,8 @@ removexattr(file_path.c_str(), name); #endif auto ret = ((res < 0) ? -errno :
 | 
			
		||||
auto remote_server::fuse_rmdir(const char *path) -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  const auto res = rmdir(file_path.c_str());
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
  auto res = rmdir(file_path.c_str());
 | 
			
		||||
  if (res == 0) {
 | 
			
		||||
    directory_cache_.remove_directory(utils::path::create_api_path(path));
 | 
			
		||||
  }
 | 
			
		||||
@@ -785,7 +786,7 @@ auto remote_server::fuse_setattr_x(const char *path, remote::setattr_x &attr)
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
  auto ret = fuse_fsetattr_x(
 | 
			
		||||
      path, attr, static_cast<remote::file_handle>(REPERTORY_INVALID_HANDLE));
 | 
			
		||||
  RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, ret);
 | 
			
		||||
@@ -797,8 +798,8 @@ auto remote_server::fuse_setbkuptime(const char *path,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
 | 
			
		||||
  auto ret = -EACCES;
 | 
			
		||||
  if (drive_.check_parent_access(api_path, X_OK) == api_error::success) {
 | 
			
		||||
@@ -818,8 +819,8 @@ auto remote_server::fuse_setchgtime(const char *path,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
 | 
			
		||||
  auto ret = -EACCES;
 | 
			
		||||
  if (drive_.check_parent_access(api_path, X_OK) == api_error::success) {
 | 
			
		||||
@@ -839,8 +840,8 @@ auto remote_server::fuse_setcrtime(const char *path,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
 | 
			
		||||
  auto ret = -EACCES;
 | 
			
		||||
  if (drive_.check_parent_access(api_path, X_OK) == api_error::success) {
 | 
			
		||||
@@ -886,11 +887,11 @@ auto remote_server::fuse_statfs(const char *path, std::uint64_t frsize,
 | 
			
		||||
                                remote::statfs &r_stat) -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
 | 
			
		||||
  const auto total_bytes = drive_.get_total_drive_space();
 | 
			
		||||
  const auto total_used = drive_.get_used_drive_space();
 | 
			
		||||
  const auto used_blocks = utils::divide_with_ceiling(total_used, frsize);
 | 
			
		||||
  auto total_bytes = drive_.get_total_drive_space();
 | 
			
		||||
  auto total_used = drive_.get_used_drive_space();
 | 
			
		||||
  auto used_blocks = utils::divide_with_ceiling(total_used, frsize);
 | 
			
		||||
  r_stat.f_files = 4294967295;
 | 
			
		||||
  r_stat.f_blocks = utils::divide_with_ceiling(total_bytes, frsize);
 | 
			
		||||
  r_stat.f_bavail = r_stat.f_bfree =
 | 
			
		||||
@@ -907,11 +908,11 @@ auto remote_server::fuse_statfs_x(const char *path, std::uint64_t bsize,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
 | 
			
		||||
  const auto total_bytes = drive_.get_total_drive_space();
 | 
			
		||||
  const auto total_used = drive_.get_used_drive_space();
 | 
			
		||||
  const auto used_blocks = utils::divide_with_ceiling(total_used, bsize);
 | 
			
		||||
  auto total_bytes = drive_.get_total_drive_space();
 | 
			
		||||
  auto total_used = drive_.get_used_drive_space();
 | 
			
		||||
  auto used_blocks = utils::divide_with_ceiling(total_used, bsize);
 | 
			
		||||
  r_stat.f_files = 4294967295;
 | 
			
		||||
  r_stat.f_blocks = utils::divide_with_ceiling(total_bytes, bsize);
 | 
			
		||||
  r_stat.f_bavail = r_stat.f_bfree =
 | 
			
		||||
@@ -932,8 +933,8 @@ auto remote_server::fuse_truncate(const char *path,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  const auto res = truncate(file_path.c_str(), static_cast<off_t>(size));
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
  auto res = truncate(file_path.c_str(), static_cast<off_t>(size));
 | 
			
		||||
  auto ret = ((res < 0) ? -errno : 0);
 | 
			
		||||
  RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, ret);
 | 
			
		||||
  return ret;
 | 
			
		||||
@@ -942,8 +943,8 @@ auto remote_server::fuse_truncate(const char *path,
 | 
			
		||||
auto remote_server::fuse_unlink(const char *path) -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  const auto res = unlink(file_path.c_str());
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
  auto res = unlink(file_path.c_str());
 | 
			
		||||
  auto ret = ((res < 0) ? -errno : 0);
 | 
			
		||||
  RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, ret);
 | 
			
		||||
  return ret;
 | 
			
		||||
@@ -954,7 +955,7 @@ auto remote_server::fuse_utimens(const char *path, const remote::file_time *tv,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
 | 
			
		||||
  struct timespec tv2[2] = {{0, 0}};
 | 
			
		||||
  const auto process_timespec = [](auto op, const auto &src, auto &dst) {
 | 
			
		||||
@@ -971,8 +972,7 @@ auto remote_server::fuse_utimens(const char *path, const remote::file_time *tv,
 | 
			
		||||
  process_timespec(op0, tv[0U], tv2[0U]);
 | 
			
		||||
  process_timespec(op1, tv[1U], tv2[1U]);
 | 
			
		||||
 | 
			
		||||
  const auto res =
 | 
			
		||||
      utimensat(0, file_path.c_str(), &tv2[0U], AT_SYMLINK_NOFOLLOW);
 | 
			
		||||
  auto res = utimensat(0, file_path.c_str(), &tv2[0U], AT_SYMLINK_NOFOLLOW);
 | 
			
		||||
  auto ret = ((res < 0) ? -errno : 0);
 | 
			
		||||
  RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, ret);
 | 
			
		||||
  return ret;
 | 
			
		||||
@@ -985,7 +985,7 @@ auto remote_server::fuse_write(const char *path, const char *buffer,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
 | 
			
		||||
  ssize_t bytes_written{
 | 
			
		||||
      has_open_info(static_cast<native_handle>(handle), EBADF)};
 | 
			
		||||
@@ -1016,8 +1016,8 @@ auto remote_server::winfsp_can_delete(PVOID file_desc, PWSTR file_name)
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto relative_path = utils::string::to_utf8(file_name);
 | 
			
		||||
  const auto file_path = construct_path(relative_path);
 | 
			
		||||
  auto relative_path = utils::string::to_utf8(file_name);
 | 
			
		||||
  auto file_path = construct_path(relative_path);
 | 
			
		||||
  auto ret = static_cast<packet::error_type>(
 | 
			
		||||
      has_open_info(static_cast<native_handle>(
 | 
			
		||||
                        reinterpret_cast<remote::file_handle>(file_desc)),
 | 
			
		||||
@@ -1026,13 +1026,12 @@ auto remote_server::winfsp_can_delete(PVOID file_desc, PWSTR file_name)
 | 
			
		||||
    ret = static_cast<packet::error_type>(
 | 
			
		||||
        utils::file::directory(file_path).exists()
 | 
			
		||||
            ? drive_.get_directory_item_count(
 | 
			
		||||
                  utils::path::create_api_path(relative_path))
 | 
			
		||||
                  ? STATUS_DIRECTORY_NOT_EMPTY
 | 
			
		||||
                  : STATUS_SUCCESS
 | 
			
		||||
                  utils::path::create_api_path(relative_path)) == 0U
 | 
			
		||||
                  ? STATUS_SUCCESS
 | 
			
		||||
                  : STATUS_DIRECTORY_NOT_EMPTY
 | 
			
		||||
 | 
			
		||||
            : STATUS_SUCCESS);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, ret);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
@@ -1042,22 +1041,25 @@ auto remote_server::winfsp_cleanup(PVOID /*file_desc*/, PWSTR file_name,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto relative_path = utils::string::to_utf8(file_name);
 | 
			
		||||
  const auto file_path = construct_path(relative_path);
 | 
			
		||||
  auto relative_path = utils::string::to_utf8(file_name);
 | 
			
		||||
  auto file_path = construct_path(relative_path);
 | 
			
		||||
  was_deleted = 0U;
 | 
			
		||||
 | 
			
		||||
  const auto directory = utils::file::directory(file_path).exists();
 | 
			
		||||
  auto directory = utils::file::directory(file_path).exists();
 | 
			
		||||
  if (flags & FileSystemBase::FspCleanupDelete) {
 | 
			
		||||
    remove_all(file_path);
 | 
			
		||||
    was_deleted = 1U;
 | 
			
		||||
 | 
			
		||||
    if (directory) {
 | 
			
		||||
      if (drive_.get_directory_item_count(
 | 
			
		||||
              utils::path::create_api_path(relative_path)) == 0U) {
 | 
			
		||||
        rmdir(file_path.c_str());
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      unlink(file_path.c_str());
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    const auto api_path = utils::path::create_api_path(relative_path);
 | 
			
		||||
    auto api_path = utils::path::create_api_path(relative_path);
 | 
			
		||||
    if ((flags & FileSystemBase::FspCleanupSetArchiveBit) && not directory) {
 | 
			
		||||
      api_meta_map meta;
 | 
			
		||||
      if (drive_.get_item_meta(api_path, meta) == api_error::success) {
 | 
			
		||||
@@ -1071,7 +1073,7 @@ auto remote_server::winfsp_cleanup(PVOID /*file_desc*/, PWSTR file_name,
 | 
			
		||||
    if (flags & (FileSystemBase::FspCleanupSetLastAccessTime |
 | 
			
		||||
                 FileSystemBase::FspCleanupSetLastWriteTime |
 | 
			
		||||
                 FileSystemBase::FspCleanupSetChangeTime)) {
 | 
			
		||||
      const auto file_time_now = utils::time::get_time_now();
 | 
			
		||||
      auto file_time_now = utils::time::get_time_now();
 | 
			
		||||
      if (flags & FileSystemBase::FspCleanupSetLastAccessTime) {
 | 
			
		||||
        drive_.set_item_meta(api_path, META_ACCESSED,
 | 
			
		||||
                             std::to_string(file_time_now));
 | 
			
		||||
@@ -1100,7 +1102,7 @@ auto remote_server::winfsp_close(PVOID file_desc) -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  std::string file_path;
 | 
			
		||||
  const auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  if (has_open_info(static_cast<native_handle>(handle),
 | 
			
		||||
                    STATUS_INVALID_HANDLE) == STATUS_SUCCESS) {
 | 
			
		||||
    file_path = get_open_file_path(static_cast<native_handle>(handle));
 | 
			
		||||
@@ -1120,10 +1122,14 @@ auto remote_server::winfsp_create(PWSTR file_name, UINT32 create_options,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto relative_path = utils::string::to_utf8(file_name);
 | 
			
		||||
  const auto file_path = construct_path(relative_path);
 | 
			
		||||
  exists = utils::file::file(file_path).exists();
 | 
			
		||||
 | 
			
		||||
  auto relative_path = utils::string::to_utf8(file_name);
 | 
			
		||||
  auto file_path = construct_path(relative_path);
 | 
			
		||||
  exists = utils::file::file{file_path}.exists() ||
 | 
			
		||||
                   utils::file::directory{file_path}.exists()
 | 
			
		||||
               ? 1
 | 
			
		||||
               : 0;
 | 
			
		||||
  auto ret{static_cast<packet::error_type>(STATUS_SUCCESS)};
 | 
			
		||||
  if (exists == 0U) {
 | 
			
		||||
    if ((create_options & FILE_DIRECTORY_FILE) != 0U) {
 | 
			
		||||
      attributes |= FILE_ATTRIBUTE_DIRECTORY;
 | 
			
		||||
    } else {
 | 
			
		||||
@@ -1135,7 +1141,7 @@ auto remote_server::winfsp_create(PWSTR file_name, UINT32 create_options,
 | 
			
		||||
    std::uint32_t flags{0U};
 | 
			
		||||
    utils::windows_create_to_unix(create_options, granted_access, flags, mode);
 | 
			
		||||
 | 
			
		||||
  int res = 0;
 | 
			
		||||
    auto res{0};
 | 
			
		||||
    if ((create_options & FILE_DIRECTORY_FILE) != 0U) {
 | 
			
		||||
      res = mkdir(file_path.c_str(), mode);
 | 
			
		||||
      if (res >= 0) {
 | 
			
		||||
@@ -1156,13 +1162,15 @@ auto remote_server::winfsp_create(PWSTR file_name, UINT32 create_options,
 | 
			
		||||
                             file_path,
 | 
			
		||||
                         });
 | 
			
		||||
 | 
			
		||||
    const auto api_path = utils::path::create_api_path(relative_path);
 | 
			
		||||
      auto api_path = utils::path::create_api_path(relative_path);
 | 
			
		||||
      normalized_name = utils::string::replace_copy(api_path, '/', '\\');
 | 
			
		||||
      populate_file_info(api_path, 0, attributes, *file_info);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  auto ret = static_cast<packet::error_type>(
 | 
			
		||||
    ret = static_cast<packet::error_type>(
 | 
			
		||||
        utils::unix_error_to_windows((res < 0) ? errno : 0));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, ret);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
@@ -1171,7 +1179,7 @@ auto remote_server::winfsp_flush(PVOID file_desc, remote::file_info *file_info)
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  auto ret = static_cast<packet::error_type>(
 | 
			
		||||
      has_open_info(static_cast<native_handle>(handle), STATUS_INVALID_HANDLE));
 | 
			
		||||
  if (ret == STATUS_SUCCESS) {
 | 
			
		||||
@@ -1194,7 +1202,7 @@ auto remote_server::winfsp_get_file_info(PVOID file_desc,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  auto ret = static_cast<packet::error_type>(
 | 
			
		||||
      has_open_info(static_cast<native_handle>(handle), STATUS_INVALID_HANDLE));
 | 
			
		||||
  if (ret == STATUS_SUCCESS) {
 | 
			
		||||
@@ -1216,7 +1224,7 @@ auto remote_server::winfsp_get_security_by_name(
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  auto ret = static_cast<packet::error_type>(STATUS_SUCCESS);
 | 
			
		||||
  const auto file_path = construct_path(file_name);
 | 
			
		||||
  auto file_path = construct_path(file_name);
 | 
			
		||||
  if (utils::file::file(file_path).exists() ||
 | 
			
		||||
      (utils::file::directory(file_path).exists())) {
 | 
			
		||||
    if (attributes) {
 | 
			
		||||
@@ -1261,9 +1269,9 @@ auto remote_server::winfsp_open(PWSTR file_name, UINT32 create_options,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto relative_path = utils::string::to_utf8(file_name);
 | 
			
		||||
  const auto file_path = construct_path(relative_path);
 | 
			
		||||
  const auto directory = utils::file::directory(file_path).exists();
 | 
			
		||||
  auto relative_path = utils::string::to_utf8(file_name);
 | 
			
		||||
  auto file_path = construct_path(relative_path);
 | 
			
		||||
  auto directory = utils::file::directory(file_path).exists();
 | 
			
		||||
  if (directory) {
 | 
			
		||||
    create_options |= FILE_DIRECTORY_FILE;
 | 
			
		||||
  }
 | 
			
		||||
@@ -1283,7 +1291,7 @@ auto remote_server::winfsp_open(PWSTR file_name, UINT32 create_options,
 | 
			
		||||
                           file_path,
 | 
			
		||||
                       });
 | 
			
		||||
 | 
			
		||||
    const auto api_path = utils::path::create_api_path(relative_path);
 | 
			
		||||
    auto api_path = utils::path::create_api_path(relative_path);
 | 
			
		||||
    normalized_name = utils::string::replace_copy(api_path, '/', '\\');
 | 
			
		||||
    res = populate_file_info(api_path, *file_info);
 | 
			
		||||
    if (res != STATUS_SUCCESS) {
 | 
			
		||||
@@ -1306,20 +1314,20 @@ auto remote_server::winfsp_overwrite(PVOID file_desc, UINT32 attributes,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  auto ret = static_cast<packet::error_type>(
 | 
			
		||||
      has_open_info(static_cast<native_handle>(handle), STATUS_INVALID_HANDLE));
 | 
			
		||||
  if (ret == STATUS_SUCCESS) {
 | 
			
		||||
    const auto api_path = construct_api_path(
 | 
			
		||||
    auto api_path = construct_api_path(
 | 
			
		||||
        get_open_file_path(static_cast<native_handle>(handle)));
 | 
			
		||||
    const auto res = ftruncate(static_cast<native_handle>(handle), 0);
 | 
			
		||||
    auto res = ftruncate(static_cast<native_handle>(handle), 0);
 | 
			
		||||
    if (res >= 0) {
 | 
			
		||||
      auto set_attributes = false;
 | 
			
		||||
      if (replace_attributes) {
 | 
			
		||||
        set_attributes = true;
 | 
			
		||||
      } else if (attributes) {
 | 
			
		||||
        std::string current_attributes;
 | 
			
		||||
        const auto err =
 | 
			
		||||
        auto err =
 | 
			
		||||
            drive_.get_item_meta(api_path, META_ATTRIBUTES, current_attributes);
 | 
			
		||||
        if (err != api_error::success) {
 | 
			
		||||
          utils::error::raise_api_path_error(function_name, api_path, err,
 | 
			
		||||
@@ -1363,11 +1371,11 @@ auto remote_server::winfsp_read(PVOID file_desc, PVOID buffer, UINT64 offset,
 | 
			
		||||
 | 
			
		||||
  *bytes_transferred = 0;
 | 
			
		||||
 | 
			
		||||
  const auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  auto ret = static_cast<packet::error_type>(
 | 
			
		||||
      has_open_info(static_cast<native_handle>(handle), STATUS_INVALID_HANDLE));
 | 
			
		||||
  if (ret == STATUS_SUCCESS) {
 | 
			
		||||
    const auto res = pread64(static_cast<native_handle>(handle), buffer, length,
 | 
			
		||||
    auto res = pread64(static_cast<native_handle>(handle), buffer, length,
 | 
			
		||||
                       static_cast<off_t>(offset));
 | 
			
		||||
    if (res >= 0) {
 | 
			
		||||
      *bytes_transferred = static_cast<UINT32>(res);
 | 
			
		||||
@@ -1390,11 +1398,11 @@ auto remote_server::winfsp_read_directory(PVOID file_desc, PWSTR /*pattern*/,
 | 
			
		||||
 | 
			
		||||
  item_list.clear();
 | 
			
		||||
 | 
			
		||||
  const auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  auto ret = static_cast<packet::error_type>(
 | 
			
		||||
      has_open_info(static_cast<native_handle>(handle), STATUS_INVALID_HANDLE));
 | 
			
		||||
  if (ret == STATUS_SUCCESS) {
 | 
			
		||||
    const auto api_path = construct_api_path(
 | 
			
		||||
    auto api_path = construct_api_path(
 | 
			
		||||
        get_open_file_path(static_cast<native_handle>(handle)));
 | 
			
		||||
    directory_iterator iterator(drive_.get_directory_items(api_path));
 | 
			
		||||
    auto offset = marker == nullptr
 | 
			
		||||
@@ -1405,7 +1413,7 @@ auto remote_server::winfsp_read_directory(PVOID file_desc, PWSTR /*pattern*/,
 | 
			
		||||
    json item;
 | 
			
		||||
    while (iterator.get_json(offset++, item) == 0) {
 | 
			
		||||
      try {
 | 
			
		||||
        item_list.emplace_back(update_to_windows_format(item));
 | 
			
		||||
        item_list.emplace_back(update_to_windows_format(api_path, item));
 | 
			
		||||
      } catch (const std::exception &e) {
 | 
			
		||||
        utils::error::raise_error(function_name, e, "exception occurred");
 | 
			
		||||
      }
 | 
			
		||||
@@ -1425,25 +1433,48 @@ auto remote_server::winfsp_rename(PVOID /*file_desc*/, PWSTR file_name,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto relative_path = utils::string::to_utf8(file_name);
 | 
			
		||||
  const auto file_path = construct_path(relative_path);
 | 
			
		||||
  const auto new_relative_path = utils::string::to_utf8(new_file_name);
 | 
			
		||||
  const auto new_file_path = construct_path(new_relative_path);
 | 
			
		||||
  auto relative_path = utils::string::to_utf8(file_name);
 | 
			
		||||
  auto file_path = construct_path(relative_path);
 | 
			
		||||
  auto new_relative_path = utils::string::to_utf8(new_file_name);
 | 
			
		||||
  auto new_file_path = construct_path(new_relative_path);
 | 
			
		||||
 | 
			
		||||
  auto res = -1;
 | 
			
		||||
  packet::error_type ret{};
 | 
			
		||||
  auto res{-1};
 | 
			
		||||
  errno = ENOENT;
 | 
			
		||||
  if (utils::file::file(file_path).exists()) {
 | 
			
		||||
    res = drive_.rename_file(construct_api_path(file_path),
 | 
			
		||||
                             construct_api_path(new_file_path),
 | 
			
		||||
                             replace_if_exists != 0U);
 | 
			
		||||
  } else if (utils::file::directory(file_path).exists()) {
 | 
			
		||||
    res = drive_.rename_directory(construct_api_path(file_path),
 | 
			
		||||
                                  construct_api_path(new_file_path));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto ret = ((res < 0) ? static_cast<packet::error_type>(
 | 
			
		||||
    ret = ((res < 0) ? static_cast<packet::error_type>(
 | 
			
		||||
                           utils::unix_error_to_windows(errno))
 | 
			
		||||
                     : 0);
 | 
			
		||||
  } else {
 | 
			
		||||
    auto dir{utils::file::directory(file_path)};
 | 
			
		||||
    event_system::instance().raise<debug_log>(
 | 
			
		||||
        function_name,
 | 
			
		||||
        fmt::format("path|{}|exists|{}", file_path, dir.exists()));
 | 
			
		||||
    if (dir.exists()) {
 | 
			
		||||
      auto count{dir.count(false)};
 | 
			
		||||
      event_system::instance().raise<debug_log>(
 | 
			
		||||
          function_name, fmt::format("path|{}|count|{}", file_path, count));
 | 
			
		||||
      if (count == 0U) {
 | 
			
		||||
        res = drive_.rename_directory(construct_api_path(file_path),
 | 
			
		||||
                                      construct_api_path(new_file_path));
 | 
			
		||||
        event_system::instance().raise<debug_log>(
 | 
			
		||||
            function_name,
 | 
			
		||||
            fmt::format("path|{}|res|{}|errno|{}", file_path, res, errno));
 | 
			
		||||
        ret = ((res < 0)
 | 
			
		||||
                   ? errno == EISDIR
 | 
			
		||||
                         ? static_cast<packet::error_type>(STATUS_ACCESS_DENIED)
 | 
			
		||||
                         : static_cast<packet::error_type>(
 | 
			
		||||
                               utils::unix_error_to_windows(errno))
 | 
			
		||||
                   : 0);
 | 
			
		||||
      } else {
 | 
			
		||||
        ret = static_cast<packet::error_type>(STATUS_ACCESS_DENIED);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path + "|" + new_file_path,
 | 
			
		||||
                                 ret);
 | 
			
		||||
  return ret;
 | 
			
		||||
@@ -1455,12 +1486,11 @@ auto remote_server::winfsp_set_basic_info(
 | 
			
		||||
    remote::file_info *file_info) -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  auto ret = static_cast<packet::error_type>(
 | 
			
		||||
      has_open_info(static_cast<native_handle>(handle), STATUS_INVALID_HANDLE));
 | 
			
		||||
  if (ret == STATUS_SUCCESS) {
 | 
			
		||||
    const auto file_path =
 | 
			
		||||
        get_open_file_path(static_cast<native_handle>(handle));
 | 
			
		||||
    auto file_path = get_open_file_path(static_cast<native_handle>(handle));
 | 
			
		||||
    if (attributes == INVALID_FILE_ATTRIBUTES) {
 | 
			
		||||
      attributes = 0;
 | 
			
		||||
    } else if (attributes == 0) {
 | 
			
		||||
@@ -1469,7 +1499,7 @@ auto remote_server::winfsp_set_basic_info(
 | 
			
		||||
                       : FILE_ATTRIBUTE_ARCHIVE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto api_path = construct_api_path(file_path);
 | 
			
		||||
    auto api_path = construct_api_path(file_path);
 | 
			
		||||
    api_meta_map meta;
 | 
			
		||||
    if (attributes != 0U) {
 | 
			
		||||
      attributes &= static_cast<UINT32>(~FILE_ATTRIBUTE_NORMAL);
 | 
			
		||||
@@ -1522,11 +1552,11 @@ auto remote_server::winfsp_set_file_size(PVOID file_desc, UINT64 new_size,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  auto ret = static_cast<packet::error_type>(
 | 
			
		||||
      has_open_info(static_cast<native_handle>(handle), STATUS_INVALID_HANDLE));
 | 
			
		||||
  if (ret == STATUS_SUCCESS) {
 | 
			
		||||
    const auto res = set_allocation_size == 0U
 | 
			
		||||
    auto res = set_allocation_size == 0U
 | 
			
		||||
                   ? ftruncate(static_cast<native_handle>(handle),
 | 
			
		||||
                               static_cast<off_t>(new_size))
 | 
			
		||||
                   : 0;
 | 
			
		||||
@@ -1564,13 +1594,13 @@ auto remote_server::winfsp_write(PVOID file_desc, PVOID buffer, UINT64 offset,
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  *bytes_transferred = 0;
 | 
			
		||||
  const auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  auto handle = reinterpret_cast<remote::file_handle>(file_desc);
 | 
			
		||||
  auto ret = static_cast<packet::error_type>(
 | 
			
		||||
      has_open_info(static_cast<native_handle>(handle), STATUS_INVALID_HANDLE));
 | 
			
		||||
  if (ret == STATUS_SUCCESS) {
 | 
			
		||||
    const auto api_path = construct_api_path(
 | 
			
		||||
    auto api_path = construct_api_path(
 | 
			
		||||
        get_open_file_path(static_cast<native_handle>(handle)));
 | 
			
		||||
    const auto file_size = drive_.get_file_size(api_path);
 | 
			
		||||
    auto file_size = drive_.get_file_size(api_path);
 | 
			
		||||
    if (write_to_end != 0U) {
 | 
			
		||||
      offset = file_size;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1587,8 +1617,8 @@ auto remote_server::winfsp_write(PVOID file_desc, PVOID buffer, UINT64 offset,
 | 
			
		||||
 | 
			
		||||
    if (should_write) {
 | 
			
		||||
      if (length > 0) {
 | 
			
		||||
        const auto res = pwrite64(static_cast<native_handle>(handle), buffer,
 | 
			
		||||
                                  length, static_cast<off_t>(offset));
 | 
			
		||||
        auto res = pwrite64(static_cast<native_handle>(handle), buffer, length,
 | 
			
		||||
                            static_cast<off_t>(offset));
 | 
			
		||||
        if (res >= 0) {
 | 
			
		||||
          *bytes_transferred = static_cast<UINT32>(res);
 | 
			
		||||
          ret = populate_file_info(construct_api_path(get_open_file_path(
 | 
			
		||||
@@ -1613,8 +1643,8 @@ auto remote_server::json_create_directory_snapshot(const std::string &path,
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  const auto file_path = construct_path(api_path);
 | 
			
		||||
  auto api_path = utils::path::create_api_path(path);
 | 
			
		||||
  auto file_path = construct_path(api_path);
 | 
			
		||||
 | 
			
		||||
  auto res = -1;
 | 
			
		||||
  errno = ENOENT;
 | 
			
		||||
@@ -1645,7 +1675,7 @@ auto remote_server::json_read_directory_snapshot(
 | 
			
		||||
 | 
			
		||||
  int res{-EBADF};
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
  auto iter = directory_cache_.get_directory(handle);
 | 
			
		||||
  if (iter != nullptr) {
 | 
			
		||||
    std::size_t offset{};
 | 
			
		||||
@@ -1673,36 +1703,78 @@ auto remote_server::json_release_directory_snapshot(
 | 
			
		||||
    -> packet::error_type {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto file_path = construct_path(path);
 | 
			
		||||
  auto file_path = construct_path(path);
 | 
			
		||||
  directory_cache_.remove_directory(handle);
 | 
			
		||||
  RAISE_REMOTE_FUSE_SERVER_EVENT(function_name, file_path, 0);
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto remote_server::update_to_windows_format(json &item) -> json & {
 | 
			
		||||
  const auto api_path = item["path"].get<std::string>();
 | 
			
		||||
  item["meta"][META_ACCESSED] = std::to_string(
 | 
			
		||||
      utils::string::to_uint64(empty_as_zero(item["meta"][META_ACCESSED])));
 | 
			
		||||
  item["meta"][META_CREATION] = std::to_string(
 | 
			
		||||
      utils::string::to_uint64(empty_as_zero(item["meta"][META_CREATION])));
 | 
			
		||||
  item["meta"][META_MODIFIED] = std::to_string(
 | 
			
		||||
      utils::string::to_uint64(empty_as_zero(item["meta"][META_MODIFIED])));
 | 
			
		||||
auto remote_server::update_to_windows_format(const std::string &root_api_path,
 | 
			
		||||
                                             json &item) -> json & {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  if (item["meta"][META_WRITTEN].empty() ||
 | 
			
		||||
      (item["meta"][META_WRITTEN].get<std::string>() == "0") ||
 | 
			
		||||
      (item["meta"][META_WRITTEN].get<std::string>() ==
 | 
			
		||||
       std::to_string(utils::time::WIN32_TIME_CONVERSION))) {
 | 
			
		||||
    drive_.set_item_meta(api_path, META_WRITTEN,
 | 
			
		||||
                         item["meta"][META_MODIFIED].get<std::string>());
 | 
			
		||||
    item["meta"][META_WRITTEN] = item["meta"][META_MODIFIED];
 | 
			
		||||
  auto api_path = item[JSON_API_PATH].get<std::string>();
 | 
			
		||||
  if (api_path == "." || api_path == "..") {
 | 
			
		||||
    auto orig_api_path{api_path};
 | 
			
		||||
 | 
			
		||||
    api_path = api_path == "."
 | 
			
		||||
                   ? root_api_path
 | 
			
		||||
                   : utils::path::get_parent_api_path(root_api_path);
 | 
			
		||||
 | 
			
		||||
    api_meta_map meta;
 | 
			
		||||
    auto res = drive_.get_item_meta(api_path, meta);
 | 
			
		||||
    if (res != api_error::success) {
 | 
			
		||||
      utils::error::raise_api_path_error(
 | 
			
		||||
          function_name, api_path, res,
 | 
			
		||||
          fmt::format("failed to get '{}' meta", orig_api_path));
 | 
			
		||||
      return item;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  if (item["meta"][META_ATTRIBUTES].empty()) {
 | 
			
		||||
    item["meta"][META_ATTRIBUTES] =
 | 
			
		||||
        item["directory"].get<bool>() ? std::to_string(FILE_ATTRIBUTE_DIRECTORY)
 | 
			
		||||
                                      : std::to_string(FILE_ATTRIBUTE_ARCHIVE);
 | 
			
		||||
    drive_.set_item_meta(api_path, META_ATTRIBUTES,
 | 
			
		||||
                         item["meta"][META_ATTRIBUTES].get<std::string>());
 | 
			
		||||
    item[JSON_DIRECTORY] = utils::string::from_bool(true);
 | 
			
		||||
    item[JSON_META] = meta;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  item[JSON_META][META_ACCESSED] = std::to_string(
 | 
			
		||||
      utils::string::to_uint64(empty_as_zero(item[JSON_META][META_ACCESSED])));
 | 
			
		||||
  item[JSON_META][META_CREATION] = std::to_string(
 | 
			
		||||
      utils::string::to_uint64(empty_as_zero(item[JSON_META][META_CREATION])));
 | 
			
		||||
  item[JSON_META][META_MODIFIED] = std::to_string(
 | 
			
		||||
      utils::string::to_uint64(empty_as_zero(item[JSON_META][META_MODIFIED])));
 | 
			
		||||
 | 
			
		||||
  auto update_meta{false};
 | 
			
		||||
  if (item[JSON_META][META_WRITTEN].empty() ||
 | 
			
		||||
      (item[JSON_META][META_WRITTEN].get<std::string>() == "0") ||
 | 
			
		||||
      (item[JSON_META][META_WRITTEN].get<std::string>() ==
 | 
			
		||||
       std::to_string(utils::time::WIN32_TIME_CONVERSION))) {
 | 
			
		||||
    item[JSON_META][META_WRITTEN] =
 | 
			
		||||
        item[JSON_META][META_MODIFIED].get<std::string>();
 | 
			
		||||
    update_meta = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (item[JSON_META][META_ATTRIBUTES].empty()) {
 | 
			
		||||
    item[JSON_META][META_ATTRIBUTES] = "0";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto attributes = utils::string::to_uint32(
 | 
			
		||||
      item[JSON_META][META_ATTRIBUTES].get<std::string>());
 | 
			
		||||
  if (item[JSON_DIRECTORY].get<bool>()) {
 | 
			
		||||
    if ((attributes & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY) {
 | 
			
		||||
      attributes |= FILE_ATTRIBUTE_DIRECTORY;
 | 
			
		||||
      attributes &= (~FILE_ATTRIBUTE_ARCHIVE);
 | 
			
		||||
      item[JSON_META][META_ATTRIBUTES] = std::to_string(attributes);
 | 
			
		||||
      update_meta = true;
 | 
			
		||||
    }
 | 
			
		||||
  } else if ((attributes & FILE_ATTRIBUTE_DIRECTORY) ==
 | 
			
		||||
                 FILE_ATTRIBUTE_DIRECTORY ||
 | 
			
		||||
             attributes == 0U) {
 | 
			
		||||
    attributes |= FILE_ATTRIBUTE_ARCHIVE;
 | 
			
		||||
    attributes &= (~FILE_ATTRIBUTE_DIRECTORY);
 | 
			
		||||
    item[JSON_META][META_ATTRIBUTES] = std::to_string(attributes);
 | 
			
		||||
    update_meta = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (update_meta) {
 | 
			
		||||
    drive_.set_item_meta(api_path, item[JSON_META].get<api_meta_map>());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return item;
 | 
			
		||||
 
 | 
			
		||||
@@ -188,7 +188,7 @@ auto remote_client::winfsp_create(PWSTR file_name, UINT32 create_options,
 | 
			
		||||
    DECODE_OR_IGNORE(&response, normalized_name);
 | 
			
		||||
    DECODE_OR_IGNORE(&response, exists);
 | 
			
		||||
 | 
			
		||||
    if (ret == STATUS_SUCCESS) {
 | 
			
		||||
    if (exists == 0U) {
 | 
			
		||||
      *file_desc = reinterpret_cast<PVOID>(handle);
 | 
			
		||||
      set_open_info(to_handle(*file_desc),
 | 
			
		||||
                    open_info{
 | 
			
		||||
@@ -197,12 +197,12 @@ auto remote_client::winfsp_create(PWSTR file_name, UINT32 create_options,
 | 
			
		||||
                        {},
 | 
			
		||||
                        utils::string::to_utf8(file_name),
 | 
			
		||||
                    });
 | 
			
		||||
    }
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
      if (exists) {
 | 
			
		||||
        ::SetLastError(ERROR_ALREADY_EXISTS);
 | 
			
		||||
      }
 | 
			
		||||
#endif
 | 
			
		||||
    else {
 | 
			
		||||
      ret = STATUS_OBJECT_NAME_COLLISION;
 | 
			
		||||
    }
 | 
			
		||||
#endif // defined(_WIN32)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return ret;
 | 
			
		||||
 
 | 
			
		||||
@@ -1061,7 +1061,7 @@ auto remote_server::winfsp_get_security_by_name(PWSTR file_name,
 | 
			
		||||
  auto file_path = utils::string::from_utf8(utils::path::combine(
 | 
			
		||||
      mount_location_, {utils::string::to_utf8(file_name)}));
 | 
			
		||||
 | 
			
		||||
  auto ret = STATUS_BUFFER_OVERFLOW;
 | 
			
		||||
  auto ret{STATUS_BUFFER_OVERFLOW};
 | 
			
		||||
  if ((descriptor_size == nullptr) ||
 | 
			
		||||
      (*descriptor_size <= std::numeric_limits<SIZE_T>::max())) {
 | 
			
		||||
    auto *descriptor = descriptor_size == nullptr
 | 
			
		||||
 
 | 
			
		||||
@@ -131,14 +131,14 @@ auto remote_winfsp_drive::Create(PWSTR file_name, UINT32 create_options,
 | 
			
		||||
                                 UINT64 allocation_size, PVOID * /*file_node*/,
 | 
			
		||||
                                 PVOID *file_desc, OpenFileInfo *ofi)
 | 
			
		||||
    -> NTSTATUS {
 | 
			
		||||
  remote::file_info fi{};
 | 
			
		||||
  remote::file_info f_info{};
 | 
			
		||||
  std::string normalized_name;
 | 
			
		||||
  BOOLEAN exists = 0;
 | 
			
		||||
  BOOLEAN exists{0};
 | 
			
		||||
  auto ret = remote_instance_->winfsp_create(
 | 
			
		||||
      file_name, create_options, granted_access, attributes, allocation_size,
 | 
			
		||||
      file_desc, &fi, normalized_name, exists);
 | 
			
		||||
      file_desc, &f_info, normalized_name, exists);
 | 
			
		||||
  if (ret == STATUS_SUCCESS) {
 | 
			
		||||
    set_file_info(ofi->FileInfo, fi);
 | 
			
		||||
    set_file_info(ofi->FileInfo, f_info);
 | 
			
		||||
    auto file_path = utils::string::from_utf8(normalized_name);
 | 
			
		||||
    wcsncpy(ofi->NormalizedName, file_path.data(), wcslen(file_path.c_str()));
 | 
			
		||||
    ofi->NormalizedNameSize =
 | 
			
		||||
@@ -168,24 +168,32 @@ auto remote_winfsp_drive::GetSecurityByName(PWSTR file_name, PUINT32 attributes,
 | 
			
		||||
                                            PSECURITY_DESCRIPTOR descriptor,
 | 
			
		||||
                                            SIZE_T *descriptor_size)
 | 
			
		||||
    -> NTSTATUS {
 | 
			
		||||
  std::uint64_t sds{
 | 
			
		||||
      (descriptor_size == nullptr) ? 0U : *descriptor_size,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  std::wstring string_descriptor;
 | 
			
		||||
  std::uint64_t sds = (descriptor_size == nullptr) ? 0 : *descriptor_size;
 | 
			
		||||
  auto ret = remote_instance_->winfsp_get_security_by_name(
 | 
			
		||||
  auto ret{
 | 
			
		||||
      remote_instance_->winfsp_get_security_by_name(
 | 
			
		||||
          file_name, attributes, descriptor_size ? &sds : nullptr,
 | 
			
		||||
      string_descriptor);
 | 
			
		||||
          string_descriptor),
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if ((ret == STATUS_SUCCESS) && (descriptor_size != nullptr)) {
 | 
			
		||||
    *descriptor_size = static_cast<SIZE_T>(sds);
 | 
			
		||||
  if ((ret == STATUS_SUCCESS) && *descriptor_size) {
 | 
			
		||||
    PSECURITY_DESCRIPTOR sd{nullptr};
 | 
			
		||||
    ULONG sz2{0U};
 | 
			
		||||
 | 
			
		||||
    PSECURITY_DESCRIPTOR desc{nullptr};
 | 
			
		||||
    ULONG size{0U};
 | 
			
		||||
    if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(
 | 
			
		||||
            string_descriptor.data(), SDDL_REVISION_1, &sd, &sz2)) {
 | 
			
		||||
      if (sz2 > *descriptor_size) {
 | 
			
		||||
            string_descriptor.data(), SDDL_REVISION_1, &desc, &size)) {
 | 
			
		||||
      if (size > *descriptor_size) {
 | 
			
		||||
        ret = STATUS_BUFFER_TOO_SMALL;
 | 
			
		||||
      } else {
 | 
			
		||||
        ::CopyMemory(descriptor, sd, sz2);
 | 
			
		||||
        ::CopyMemory(descriptor, desc, size);
 | 
			
		||||
      }
 | 
			
		||||
      *descriptor_size = sz2;
 | 
			
		||||
      ::LocalFree(sd);
 | 
			
		||||
 | 
			
		||||
      *descriptor_size = size;
 | 
			
		||||
      ::LocalFree(desc);
 | 
			
		||||
    } else {
 | 
			
		||||
      ret = FspNtStatusFromWin32(::GetLastError());
 | 
			
		||||
    }
 | 
			
		||||
@@ -361,11 +369,10 @@ auto remote_winfsp_drive::ReadDirectory(PVOID /*file_node*/, PVOID file_desc,
 | 
			
		||||
              &ret)) {
 | 
			
		||||
        auto item_found = false;
 | 
			
		||||
        for (const auto &item : item_list) {
 | 
			
		||||
          auto item_path = item["path"].get<std::string>();
 | 
			
		||||
          auto item_path = item[JSON_API_PATH].get<std::string>();
 | 
			
		||||
          auto display_name = utils::string::from_utf8(
 | 
			
		||||
              utils::path::strip_to_file_name(item_path));
 | 
			
		||||
          if (not marker || (marker && item_found)) {
 | 
			
		||||
            // if (not utils::path::is_ads_file_path(item_path)) {
 | 
			
		||||
            union {
 | 
			
		||||
              UINT8 B[FIELD_OFFSET(FSP_FSCTL_DIR_INFO, FileNameBuf) +
 | 
			
		||||
                      ((repertory::max_path_length + 1U) * sizeof(WCHAR))];
 | 
			
		||||
@@ -380,10 +387,8 @@ auto remote_winfsp_drive::ReadDirectory(PVOID /*file_node*/, PVOID file_desc,
 | 
			
		||||
                          display_name.size()) *
 | 
			
		||||
                 sizeof(WCHAR)));
 | 
			
		||||
 | 
			
		||||
            if (not item["meta"].empty() ||
 | 
			
		||||
                ((item_path != ".") && (item_path != ".."))) {
 | 
			
		||||
            populate_file_info(item, directory_info->FileInfo);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (ret == STATUS_SUCCESS) {
 | 
			
		||||
              ::wcscpy_s(&directory_info->FileNameBuf[0],
 | 
			
		||||
                         repertory::max_path_length, &display_name[0]);
 | 
			
		||||
@@ -394,7 +399,6 @@ auto remote_winfsp_drive::ReadDirectory(PVOID /*file_node*/, PVOID file_desc,
 | 
			
		||||
                break;
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            // }
 | 
			
		||||
          } else {
 | 
			
		||||
            item_found = display_name == std::wstring(marker);
 | 
			
		||||
          }
 | 
			
		||||
@@ -418,8 +422,13 @@ auto remote_winfsp_drive::ReadDirectory(PVOID /*file_node*/, PVOID file_desc,
 | 
			
		||||
auto remote_winfsp_drive::Rename(PVOID /*file_node*/, PVOID file_desc,
 | 
			
		||||
                                 PWSTR file_name, PWSTR new_file_name,
 | 
			
		||||
                                 BOOLEAN replace_if_exists) -> NTSTATUS {
 | 
			
		||||
  return remote_instance_->winfsp_rename(file_desc, file_name, new_file_name,
 | 
			
		||||
                                         replace_if_exists);
 | 
			
		||||
  auto res = remote_instance_->winfsp_rename(file_desc, file_name,
 | 
			
		||||
                                             new_file_name, replace_if_exists);
 | 
			
		||||
  if (res == STATUS_OBJECT_NAME_EXISTS) {
 | 
			
		||||
    return FspNtStatusFromWin32(ERROR_ALREADY_EXISTS);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto remote_winfsp_drive::SetBasicInfo(PVOID /*file_node*/, PVOID file_desc,
 | 
			
		||||
@@ -428,11 +437,11 @@ auto remote_winfsp_drive::SetBasicInfo(PVOID /*file_node*/, PVOID file_desc,
 | 
			
		||||
                                       UINT64 last_write_time,
 | 
			
		||||
                                       UINT64 change_time, FileInfo *file_info)
 | 
			
		||||
    -> NTSTATUS {
 | 
			
		||||
  remote::file_info fi{};
 | 
			
		||||
  remote::file_info f_info{};
 | 
			
		||||
  auto ret = remote_instance_->winfsp_set_basic_info(
 | 
			
		||||
      file_desc, attributes, creation_time, last_access_time, last_write_time,
 | 
			
		||||
      change_time, &fi);
 | 
			
		||||
  set_file_info(*file_info, fi);
 | 
			
		||||
      change_time, &f_info);
 | 
			
		||||
  set_file_info(*file_info, f_info);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -473,7 +482,6 @@ VOID remote_winfsp_drive::Unmounted(PVOID host) {
 | 
			
		||||
  }
 | 
			
		||||
  remote_instance_->winfsp_unmounted(file_system_host->MountPoint());
 | 
			
		||||
  remote_instance_.reset();
 | 
			
		||||
  mount_location_ = "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto remote_winfsp_drive::Write(PVOID /*file_node*/, PVOID file_desc,
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@
 | 
			
		||||
#include "events/types/invalid_cache_size.hpp"
 | 
			
		||||
#include "events/types/max_cache_size_reached.hpp"
 | 
			
		||||
#include "types/startup_exception.hpp"
 | 
			
		||||
#include "utils/file_utils.hpp"
 | 
			
		||||
#include "utils/file.hpp"
 | 
			
		||||
 | 
			
		||||
namespace repertory {
 | 
			
		||||
cache_size_mgr cache_size_mgr::instance_{};
 | 
			
		||||
@@ -49,16 +49,15 @@ auto cache_size_mgr::expand(std::uint64_t size) -> api_error {
 | 
			
		||||
  auto last_cache_size{cache_size_};
 | 
			
		||||
  cache_size_ += size;
 | 
			
		||||
 | 
			
		||||
  auto max_cache_size{cfg_->get_max_cache_size_bytes()};
 | 
			
		||||
 | 
			
		||||
  auto cache_dir{
 | 
			
		||||
      utils::file::directory{cfg_->get_cache_directory()},
 | 
			
		||||
  };
 | 
			
		||||
  while (not get_stop_requested() && cache_size_ > max_cache_size &&
 | 
			
		||||
  while (not get_stop_requested() &&
 | 
			
		||||
         cache_size_ > cfg_->get_max_cache_size_bytes() &&
 | 
			
		||||
         cache_dir.count() > 1U) {
 | 
			
		||||
    if (last_cache_size != cache_size_) {
 | 
			
		||||
      event_system::instance().raise<max_cache_size_reached>(
 | 
			
		||||
          cache_size_, function_name, max_cache_size);
 | 
			
		||||
          cache_size_, function_name, cfg_->get_max_cache_size_bytes());
 | 
			
		||||
      last_cache_size = cache_size_;
 | 
			
		||||
    }
 | 
			
		||||
    notify_.wait_for(lock, cache_wait_secs);
 | 
			
		||||
 
 | 
			
		||||
@@ -21,9 +21,8 @@
 | 
			
		||||
*/
 | 
			
		||||
#if !defined(_WIN32)
 | 
			
		||||
 | 
			
		||||
#include "platform/unix_platform.hpp"
 | 
			
		||||
#include "platform/platform.hpp"
 | 
			
		||||
 | 
			
		||||
#include "app_config.hpp"
 | 
			
		||||
#include "events/event_system.hpp"
 | 
			
		||||
#include "events/types/filesystem_item_added.hpp"
 | 
			
		||||
#include "providers/i_provider.hpp"
 | 
			
		||||
@@ -36,61 +35,65 @@
 | 
			
		||||
#include "utils/unix.hpp"
 | 
			
		||||
 | 
			
		||||
namespace repertory {
 | 
			
		||||
lock_data::lock_data(const provider_type &pt, std::string unique_id /*= ""*/)
 | 
			
		||||
    : pt_(pt),
 | 
			
		||||
      unique_id_(std::move(unique_id)),
 | 
			
		||||
      mutex_id_("repertory_" + app_config::get_provider_name(pt) + "_" +
 | 
			
		||||
                unique_id_) {
 | 
			
		||||
  lock_fd_ = open(get_lock_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
 | 
			
		||||
lock_data::lock_data(provider_type prov, std::string_view unique_id)
 | 
			
		||||
    : mutex_id_(create_lock_id(prov, unique_id)) {
 | 
			
		||||
  handle_ = open(get_lock_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
lock_data::lock_data()
 | 
			
		||||
    : pt_(provider_type::sia), unique_id_(""), mutex_id_(""), lock_fd_(-1) {}
 | 
			
		||||
lock_data::~lock_data() { release(); }
 | 
			
		||||
 | 
			
		||||
lock_data::~lock_data() {
 | 
			
		||||
  if (lock_fd_ != -1) {
 | 
			
		||||
    if (lock_status_ == 0) {
 | 
			
		||||
      unlink(get_lock_file().c_str());
 | 
			
		||||
      flock(lock_fd_, LOCK_UN);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    close(lock_fd_);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto lock_data::get_lock_data_file() -> std::string {
 | 
			
		||||
  const auto dir = get_state_directory();
 | 
			
		||||
auto lock_data::get_lock_data_file() const -> std::string {
 | 
			
		||||
  auto dir = get_state_directory();
 | 
			
		||||
  if (not utils::file::directory(dir).create_directory()) {
 | 
			
		||||
    throw startup_exception("failed to create directory|sp|" + dir + "|err|" +
 | 
			
		||||
                            std::to_string(utils::get_last_error_code()));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return utils::path::combine(
 | 
			
		||||
      dir, {"mountstate_" + std::to_string(getuid()) + ".json"});
 | 
			
		||||
      dir, {
 | 
			
		||||
               fmt::format("{}_{}.json", mutex_id_, getuid()),
 | 
			
		||||
           });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto lock_data::get_lock_file() -> std::string {
 | 
			
		||||
  const auto dir = get_state_directory();
 | 
			
		||||
auto lock_data::get_lock_file() const -> std::string {
 | 
			
		||||
  auto dir = get_state_directory();
 | 
			
		||||
  if (not utils::file::directory(dir).create_directory()) {
 | 
			
		||||
    throw startup_exception("failed to create directory|sp|" + dir + "|err|" +
 | 
			
		||||
                            std::to_string(utils::get_last_error_code()));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return utils::path::combine(dir,
 | 
			
		||||
                              {mutex_id_ + "_" + std::to_string(getuid())});
 | 
			
		||||
  return utils::path::combine(
 | 
			
		||||
      dir, {
 | 
			
		||||
               fmt::format("{}_{}.lock", mutex_id_, getuid()),
 | 
			
		||||
           });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto lock_data::get_mount_state(json &mount_state) -> bool {
 | 
			
		||||
  auto ret = false;
 | 
			
		||||
  auto fd =
 | 
			
		||||
      open(get_lock_data_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
 | 
			
		||||
  if (fd != -1) {
 | 
			
		||||
    if (wait_for_lock(fd) == 0) {
 | 
			
		||||
      ret = utils::file::read_json_file(get_lock_data_file(), mount_state);
 | 
			
		||||
      flock(fd, LOCK_UN);
 | 
			
		||||
  auto handle = open(get_lock_data_file().c_str(), O_RDWR, S_IWUSR | S_IRUSR);
 | 
			
		||||
  if (handle == -1) {
 | 
			
		||||
    mount_state = {
 | 
			
		||||
        {"Active", false},
 | 
			
		||||
        {"Location", ""},
 | 
			
		||||
        {"PID", -1},
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    close(fd);
 | 
			
		||||
  auto ret{false};
 | 
			
		||||
  if (wait_for_lock(handle) == 0) {
 | 
			
		||||
    ret = utils::file::read_json_file(get_lock_data_file(), mount_state);
 | 
			
		||||
    if (ret && mount_state.empty()) {
 | 
			
		||||
      mount_state = {
 | 
			
		||||
          {"Active", false},
 | 
			
		||||
          {"Location", ""},
 | 
			
		||||
          {"PID", -1},
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
    flock(handle, LOCK_UN);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  close(handle);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -98,25 +101,20 @@ auto lock_data::get_state_directory() -> std::string {
 | 
			
		||||
#if defined(__APPLE__)
 | 
			
		||||
  return utils::path::absolute("~/Library/Application Support/" +
 | 
			
		||||
                               std::string{REPERTORY_DATA_NAME} + "/state");
 | 
			
		||||
#else
 | 
			
		||||
#else  // !defined(__APPLE__)
 | 
			
		||||
  return utils::path::absolute("~/.local/" + std::string{REPERTORY_DATA_NAME} +
 | 
			
		||||
                               "/state");
 | 
			
		||||
#endif
 | 
			
		||||
#endif // defined(__APPLE__)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  if (lock_fd_ == -1) {
 | 
			
		||||
  if (handle_ == -1) {
 | 
			
		||||
    return lock_result::failure;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  lock_status_ = wait_for_lock(lock_fd_, retry_count);
 | 
			
		||||
  lock_status_ = wait_for_lock(handle_, retry_count);
 | 
			
		||||
  switch (lock_status_) {
 | 
			
		||||
  case 0:
 | 
			
		||||
    if (not set_mount_state(false, "", -1)) {
 | 
			
		||||
      utils::error::raise_error(function_name, "failed to set mount state");
 | 
			
		||||
    }
 | 
			
		||||
    return lock_result::success;
 | 
			
		||||
  case EWOULDBLOCK:
 | 
			
		||||
    return lock_result::locked;
 | 
			
		||||
@@ -125,42 +123,54 @@ auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto lock_data::set_mount_state(bool active, const std::string &mount_location,
 | 
			
		||||
void lock_data::release() {
 | 
			
		||||
  if (handle_ == -1) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (lock_status_ == 0) {
 | 
			
		||||
    [[maybe_unused]] auto success{utils::file::file{get_lock_file()}.remove()};
 | 
			
		||||
    flock(handle_, LOCK_UN);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  close(handle_);
 | 
			
		||||
  handle_ = -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto lock_data::set_mount_state(bool active, std::string_view mount_location,
 | 
			
		||||
                                int pid) -> bool {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  auto ret = false;
 | 
			
		||||
  auto handle =
 | 
			
		||||
      open(get_lock_data_file().c_str(), O_CREAT | O_RDWR, S_IWUSR | S_IRUSR);
 | 
			
		||||
  if (handle != -1) {
 | 
			
		||||
  if (handle == -1) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto ret{false};
 | 
			
		||||
  if (wait_for_lock(handle) == 0) {
 | 
			
		||||
      const auto mount_id =
 | 
			
		||||
          app_config::get_provider_display_name(pt_) + unique_id_;
 | 
			
		||||
    json mount_state;
 | 
			
		||||
    if (not utils::file::read_json_file(get_lock_data_file(), mount_state)) {
 | 
			
		||||
      utils::error::raise_error(function_name,
 | 
			
		||||
                                "failed to read mount state file|sp|" +
 | 
			
		||||
                                    get_lock_file());
 | 
			
		||||
    }
 | 
			
		||||
      if ((mount_state.find(mount_id) == mount_state.end()) ||
 | 
			
		||||
          (mount_state[mount_id].find("Active") ==
 | 
			
		||||
           mount_state[mount_id].end()) ||
 | 
			
		||||
          (mount_state[mount_id]["Active"].get<bool>() != active) ||
 | 
			
		||||
          (active && ((mount_state[mount_id].find("Location") ==
 | 
			
		||||
                       mount_state[mount_id].end()) ||
 | 
			
		||||
                      (mount_state[mount_id]["Location"].get<std::string>() !=
 | 
			
		||||
                       mount_location)))) {
 | 
			
		||||
        const auto lines = utils::file::read_file_lines(get_lock_data_file());
 | 
			
		||||
        const auto txt = std::accumulate(
 | 
			
		||||
            lines.begin(), lines.end(), std::string(),
 | 
			
		||||
            [](auto &&val, auto &&line) -> auto { return val + line; });
 | 
			
		||||
        auto json_data = json::parse(txt.empty() ? "{}" : txt);
 | 
			
		||||
        json_data[mount_id] = {
 | 
			
		||||
    if ((mount_state.find("Active") == mount_state.end()) ||
 | 
			
		||||
        (mount_state["Active"].get<bool>() != active) ||
 | 
			
		||||
        (active &&
 | 
			
		||||
         ((mount_state.find("Location") == mount_state.end()) ||
 | 
			
		||||
          (mount_state["Location"].get<std::string>() != mount_location)))) {
 | 
			
		||||
      if (mount_location.empty() && not active) {
 | 
			
		||||
        ret = utils::file::file{get_lock_data_file()}.remove();
 | 
			
		||||
      } else {
 | 
			
		||||
        ret = utils::file::write_json_file(
 | 
			
		||||
            get_lock_data_file(),
 | 
			
		||||
            {
 | 
			
		||||
                {"Active", active},
 | 
			
		||||
                {"Location", active ? mount_location : ""},
 | 
			
		||||
                {"PID", active ? pid : -1},
 | 
			
		||||
        };
 | 
			
		||||
        ret = utils::file::write_json_file(get_lock_data_file(), json_data);
 | 
			
		||||
            });
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      ret = true;
 | 
			
		||||
    }
 | 
			
		||||
@@ -169,17 +179,16 @@ auto lock_data::set_mount_state(bool active, const std::string &mount_location,
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  close(handle);
 | 
			
		||||
  }
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto lock_data::wait_for_lock(int fd, std::uint8_t retry_count) -> int {
 | 
			
		||||
  static constexpr const std::uint32_t max_sleep = 100U;
 | 
			
		||||
auto lock_data::wait_for_lock(int handle, std::uint8_t retry_count) -> int {
 | 
			
		||||
  static constexpr const std::uint32_t max_sleep{100U};
 | 
			
		||||
 | 
			
		||||
  auto lock_status = EWOULDBLOCK;
 | 
			
		||||
  auto remain = static_cast<std::uint32_t>(retry_count * max_sleep);
 | 
			
		||||
  auto lock_status{EWOULDBLOCK};
 | 
			
		||||
  auto remain{static_cast<std::uint32_t>(retry_count * max_sleep)};
 | 
			
		||||
  while ((remain > 0) && (lock_status == EWOULDBLOCK)) {
 | 
			
		||||
    lock_status = flock(fd, LOCK_EX | LOCK_NB);
 | 
			
		||||
    lock_status = flock(handle, LOCK_EX | LOCK_NB);
 | 
			
		||||
    if (lock_status == -1) {
 | 
			
		||||
      lock_status = errno;
 | 
			
		||||
      if (lock_status == EWOULDBLOCK) {
 | 
			
		||||
@@ -228,13 +237,13 @@ auto provider_meta_handler(i_provider &provider, bool directory,
 | 
			
		||||
                           const api_file &file) -> api_error {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  const auto meta = create_meta_attributes(
 | 
			
		||||
  auto meta = create_meta_attributes(
 | 
			
		||||
      file.accessed_date,
 | 
			
		||||
      directory ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_ARCHIVE,
 | 
			
		||||
      file.changed_date, file.creation_date, directory, getgid(), file.key,
 | 
			
		||||
      directory ? S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR
 | 
			
		||||
                : S_IFREG | S_IRUSR | S_IWUSR,
 | 
			
		||||
      file.modified_date, 0u, 0u, file.file_size, file.source_path, getuid(),
 | 
			
		||||
      file.modified_date, 0U, 0U, file.file_size, file.source_path, getuid(),
 | 
			
		||||
      file.modified_date);
 | 
			
		||||
  auto res = provider.set_item_meta(file.api_path, meta);
 | 
			
		||||
  if (res == api_error::success) {
 | 
			
		||||
 
 | 
			
		||||
@@ -21,150 +21,171 @@
 | 
			
		||||
*/
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
 | 
			
		||||
#include "platform/win32_platform.hpp"
 | 
			
		||||
#include "platform/platform.hpp"
 | 
			
		||||
 | 
			
		||||
#include "events/event_system.hpp"
 | 
			
		||||
#include "events/types/filesystem_item_added.hpp"
 | 
			
		||||
#include "providers/i_provider.hpp"
 | 
			
		||||
#include "utils/config.hpp"
 | 
			
		||||
#include "utils/error_utils.hpp"
 | 
			
		||||
#include "utils/string.hpp"
 | 
			
		||||
 | 
			
		||||
namespace repertory {
 | 
			
		||||
auto lock_data::get_mount_state(const provider_type & /*pt*/, json &mount_state)
 | 
			
		||||
    -> bool {
 | 
			
		||||
  const auto ret = get_mount_state(mount_state);
 | 
			
		||||
  if (ret) {
 | 
			
		||||
    const auto mount_id =
 | 
			
		||||
        app_config::get_provider_display_name(pt_) + unique_id_;
 | 
			
		||||
    mount_state = mount_state[mount_id].empty()
 | 
			
		||||
                      ? json({{"Active", false}, {"Location", ""}, {"PID", -1}})
 | 
			
		||||
                      : mount_state[mount_id];
 | 
			
		||||
lock_data::lock_data(provider_type prov, std::string unique_id)
 | 
			
		||||
    : mutex_id_(create_lock_id(prov, unique_id)),
 | 
			
		||||
      mutex_handle_(::CreateMutex(nullptr, FALSE,
 | 
			
		||||
                                  create_lock_id(prov, unique_id).c_str())) {}
 | 
			
		||||
 | 
			
		||||
lock_data::~lock_data() { release(); }
 | 
			
		||||
 | 
			
		||||
auto lock_data::get_current_mount_state(json &mount_state) -> bool {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  HKEY key{};
 | 
			
		||||
  if (::RegOpenKeyEx(HKEY_CURRENT_USER,
 | 
			
		||||
                     fmt::format(R"(SOFTWARE\{}\Mounts\{})",
 | 
			
		||||
                                 REPERTORY_DATA_NAME, mutex_id_)
 | 
			
		||||
                         .c_str(),
 | 
			
		||||
                     0, KEY_ALL_ACCESS, &key) != ERROR_SUCCESS) {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::string data;
 | 
			
		||||
  DWORD data_size{};
 | 
			
		||||
 | 
			
		||||
  DWORD type{REG_SZ};
 | 
			
		||||
  ::RegGetValueA(key, nullptr, nullptr, RRF_RT_REG_SZ, &type, nullptr,
 | 
			
		||||
                 &data_size);
 | 
			
		||||
 | 
			
		||||
  data.resize(data_size);
 | 
			
		||||
  auto res = ::RegGetValueA(key, nullptr, nullptr, RRF_RT_REG_SZ, &type,
 | 
			
		||||
                            data.data(), &data_size);
 | 
			
		||||
  auto ret = res == ERROR_SUCCESS || res == ERROR_FILE_NOT_FOUND;
 | 
			
		||||
  if (ret && data_size != 0U) {
 | 
			
		||||
    try {
 | 
			
		||||
      mount_state = json::parse(data);
 | 
			
		||||
    } catch (const std::exception &e) {
 | 
			
		||||
      utils::error::raise_error(function_name, e, "failed to read mount state");
 | 
			
		||||
      ret = false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ::RegCloseKey(key);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto lock_data::get_mount_state(json &mount_state) -> bool {
 | 
			
		||||
  HKEY key;
 | 
			
		||||
  auto ret = !::RegCreateKeyEx(
 | 
			
		||||
      HKEY_CURRENT_USER,
 | 
			
		||||
      ("SOFTWARE\\" + std::string{REPERTORY_DATA_NAME} + "\\Mounts").c_str(), 0,
 | 
			
		||||
      nullptr, 0, KEY_ALL_ACCESS, nullptr, &key, nullptr);
 | 
			
		||||
  if (ret) {
 | 
			
		||||
    DWORD i = 0u;
 | 
			
		||||
    DWORD data_size = 0u;
 | 
			
		||||
    std::string name;
 | 
			
		||||
    name.resize(32767u);
 | 
			
		||||
    auto name_size = static_cast<DWORD>(name.size());
 | 
			
		||||
    while (ret &&
 | 
			
		||||
           (::RegEnumValue(key, i, &name[0], &name_size, nullptr, nullptr,
 | 
			
		||||
                           nullptr, &data_size) == ERROR_SUCCESS)) {
 | 
			
		||||
      std::string data;
 | 
			
		||||
      data.resize(data_size);
 | 
			
		||||
      name_size++;
 | 
			
		||||
      if ((ret = !::RegEnumValue(key, i++, &name[0], &name_size, nullptr,
 | 
			
		||||
                                 nullptr, reinterpret_cast<LPBYTE>(&data[0]),
 | 
			
		||||
                                 &data_size))) {
 | 
			
		||||
        mount_state[name.c_str()] = json::parse(data);
 | 
			
		||||
        name_size = static_cast<DWORD>(name.size());
 | 
			
		||||
        data_size = 0u;
 | 
			
		||||
  if (not get_current_mount_state(mount_state)) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
    }
 | 
			
		||||
    ::RegCloseKey(key);
 | 
			
		||||
  }
 | 
			
		||||
  return ret;
 | 
			
		||||
 | 
			
		||||
  mount_state = mount_state.empty() ? json({
 | 
			
		||||
                                          {"Active", false},
 | 
			
		||||
                                          {"Location", ""},
 | 
			
		||||
                                          {"PID", -1},
 | 
			
		||||
                                      })
 | 
			
		||||
                                    : mount_state;
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto lock_data::grab_lock(std::uint8_t retry_count) -> lock_result {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
  static constexpr const std::uint32_t max_sleep{100U};
 | 
			
		||||
 | 
			
		||||
  auto ret = lock_result::success;
 | 
			
		||||
  if (mutex_handle_ == INVALID_HANDLE_VALUE) {
 | 
			
		||||
    ret = lock_result::failure;
 | 
			
		||||
  } else {
 | 
			
		||||
    for (auto i = 0;
 | 
			
		||||
         (i <= retry_count) && ((mutex_state_ = ::WaitForSingleObject(
 | 
			
		||||
                                     mutex_handle_, 100)) == WAIT_TIMEOUT);
 | 
			
		||||
         i++) {
 | 
			
		||||
    return lock_result::failure;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (std::uint8_t idx = 0U;
 | 
			
		||||
       (idx <= retry_count) &&
 | 
			
		||||
       ((mutex_state_ = ::WaitForSingleObject(mutex_handle_, max_sleep)) ==
 | 
			
		||||
        WAIT_TIMEOUT);
 | 
			
		||||
       ++idx) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  switch (mutex_state_) {
 | 
			
		||||
    case WAIT_OBJECT_0: {
 | 
			
		||||
      ret = lock_result::success;
 | 
			
		||||
      auto should_reset = true;
 | 
			
		||||
      json mount_state;
 | 
			
		||||
      if (get_mount_state(pt_, mount_state)) {
 | 
			
		||||
        if (mount_state["Active"].get<bool>() &&
 | 
			
		||||
            mount_state["Location"] == "elevating") {
 | 
			
		||||
          should_reset = false;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (should_reset) {
 | 
			
		||||
        if (not set_mount_state(false, "", -1)) {
 | 
			
		||||
          utils::error::raise_error(function_name, "failed to set mount state");
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } break;
 | 
			
		||||
  case WAIT_OBJECT_0:
 | 
			
		||||
    return lock_result::success;
 | 
			
		||||
 | 
			
		||||
  case WAIT_TIMEOUT:
 | 
			
		||||
      ret = lock_result::locked;
 | 
			
		||||
      break;
 | 
			
		||||
    return lock_result::locked;
 | 
			
		||||
 | 
			
		||||
  default:
 | 
			
		||||
      ret = lock_result::failure;
 | 
			
		||||
      break;
 | 
			
		||||
    return lock_result::failure;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void lock_data::release() {
 | 
			
		||||
  if (mutex_handle_ != INVALID_HANDLE_VALUE) {
 | 
			
		||||
  if (mutex_handle_ == INVALID_HANDLE_VALUE) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if ((mutex_state_ == WAIT_OBJECT_0) || (mutex_state_ == WAIT_ABANDONED)) {
 | 
			
		||||
    if (mutex_state_ == WAIT_OBJECT_0) {
 | 
			
		||||
      [[maybe_unused]] auto success{set_mount_state(false, "", -1)};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ::ReleaseMutex(mutex_handle_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ::CloseHandle(mutex_handle_);
 | 
			
		||||
  mutex_handle_ = INVALID_HANDLE_VALUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto lock_data::set_mount_state(bool active, std::string_view mount_location,
 | 
			
		||||
                                std::int64_t pid) -> bool {
 | 
			
		||||
  if (mutex_handle_ == INVALID_HANDLE_VALUE) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
auto lock_data::set_mount_state(bool active, const std::string &mount_location,
 | 
			
		||||
                                const std::int64_t &pid) -> bool {
 | 
			
		||||
  auto ret = false;
 | 
			
		||||
  if (mutex_handle_ != INVALID_HANDLE_VALUE) {
 | 
			
		||||
    const auto mount_id =
 | 
			
		||||
        app_config::get_provider_display_name(pt_) + unique_id_;
 | 
			
		||||
  json mount_state;
 | 
			
		||||
    [[maybe_unused]] auto success = get_mount_state(mount_state);
 | 
			
		||||
    if ((mount_state.find(mount_id) == mount_state.end()) ||
 | 
			
		||||
        (mount_state[mount_id].find("Active") == mount_state[mount_id].end()) ||
 | 
			
		||||
        (mount_state[mount_id]["Active"].get<bool>() != active) ||
 | 
			
		||||
        (active && ((mount_state[mount_id].find("Location") ==
 | 
			
		||||
                     mount_state[mount_id].end()) ||
 | 
			
		||||
                    (mount_state[mount_id]["Location"].get<std::string>() !=
 | 
			
		||||
                     mount_location)))) {
 | 
			
		||||
      HKEY key;
 | 
			
		||||
      if ((ret = !::RegCreateKeyEx(
 | 
			
		||||
               HKEY_CURRENT_USER,
 | 
			
		||||
               ("SOFTWARE\\" + std::string{REPERTORY_DATA_NAME} + "\\Mounts")
 | 
			
		||||
                   .c_str(),
 | 
			
		||||
               0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &key, nullptr))) {
 | 
			
		||||
        const auto str = json({{"Active", active},
 | 
			
		||||
                               {"Location", active ? mount_location : ""},
 | 
			
		||||
                               {"PID", active ? pid : -1}})
 | 
			
		||||
                             .dump(0);
 | 
			
		||||
        ret = !::RegSetValueEx(key, &mount_id[0], 0, REG_SZ,
 | 
			
		||||
                               reinterpret_cast<const BYTE *>(&str[0]),
 | 
			
		||||
                               static_cast<DWORD>(str.size()));
 | 
			
		||||
        ::RegCloseKey(key);
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      ret = true;
 | 
			
		||||
    }
 | 
			
		||||
  [[maybe_unused]] auto success{get_mount_state(mount_state)};
 | 
			
		||||
  if (not((mount_state.find("Active") == mount_state.end()) ||
 | 
			
		||||
          (mount_state["Active"].get<bool>() != active) ||
 | 
			
		||||
          (active &&
 | 
			
		||||
           ((mount_state.find("Location") == mount_state.end()) ||
 | 
			
		||||
            (mount_state["Location"].get<std::string>() != mount_location))))) {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  HKEY key{};
 | 
			
		||||
  if (::RegCreateKeyExA(HKEY_CURRENT_USER,
 | 
			
		||||
                        fmt::format(R"(SOFTWARE\{}\Mounts\{})",
 | 
			
		||||
                                    REPERTORY_DATA_NAME, mutex_id_)
 | 
			
		||||
                            .c_str(),
 | 
			
		||||
                        0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &key,
 | 
			
		||||
                        nullptr) != ERROR_SUCCESS) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto ret{false};
 | 
			
		||||
  if (mount_location.empty() && not active) {
 | 
			
		||||
    ::RegCloseKey(key);
 | 
			
		||||
 | 
			
		||||
    if (::RegCreateKeyExA(
 | 
			
		||||
            HKEY_CURRENT_USER,
 | 
			
		||||
            fmt::format(R"(SOFTWARE\{}\Mounts)", REPERTORY_DATA_NAME).c_str(),
 | 
			
		||||
            0, nullptr, 0, KEY_ALL_ACCESS, nullptr, &key,
 | 
			
		||||
            nullptr) != ERROR_SUCCESS) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ret = (::RegDeleteKeyA(key, mutex_id_.c_str()) == ERROR_SUCCESS);
 | 
			
		||||
  } else {
 | 
			
		||||
    auto data{
 | 
			
		||||
        json({
 | 
			
		||||
                 {"Active", active},
 | 
			
		||||
                 {"Location", active ? mount_location : ""},
 | 
			
		||||
                 {"PID", active ? pid : -1},
 | 
			
		||||
             })
 | 
			
		||||
            .dump(),
 | 
			
		||||
    };
 | 
			
		||||
    ret = (::RegSetValueEx(key, nullptr, 0, REG_SZ,
 | 
			
		||||
                           reinterpret_cast<const BYTE *>(data.c_str()),
 | 
			
		||||
                           static_cast<DWORD>(data.size())) == ERROR_SUCCESS);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ::RegCloseKey(key);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -215,4 +236,4 @@ auto provider_meta_handler(i_provider &provider, bool directory,
 | 
			
		||||
}
 | 
			
		||||
} // namespace repertory
 | 
			
		||||
 | 
			
		||||
#endif //_WIN32
 | 
			
		||||
#endif // defined(_WIN32)
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,6 @@
 | 
			
		||||
#include "utils/file_utils.hpp"
 | 
			
		||||
#include "utils/path.hpp"
 | 
			
		||||
#include "utils/polling.hpp"
 | 
			
		||||
#include <spdlog/fmt/bundled/base.h>
 | 
			
		||||
 | 
			
		||||
namespace repertory {
 | 
			
		||||
encrypt_provider::encrypt_provider(app_config &config)
 | 
			
		||||
@@ -225,8 +224,7 @@ auto encrypt_provider::get_directory_items(const std::string &api_path,
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (result == api_error::directory_not_found) {
 | 
			
		||||
                  process_directory_entry(*dir_entry.get(), cfg,
 | 
			
		||||
                                          current_api_path);
 | 
			
		||||
                  process_directory_entry(*dir_entry, cfg, current_api_path);
 | 
			
		||||
 | 
			
		||||
                  result = db_->get_directory_api_path(dir_entry->get_path(),
 | 
			
		||||
                                                       current_api_path);
 | 
			
		||||
@@ -247,7 +245,7 @@ auto encrypt_provider::get_directory_items(const std::string &api_path,
 | 
			
		||||
                  continue;
 | 
			
		||||
                }
 | 
			
		||||
                if (result == api_error::item_not_found &&
 | 
			
		||||
                    not process_directory_entry(*dir_entry.get(), cfg,
 | 
			
		||||
                    not process_directory_entry(*dir_entry, cfg,
 | 
			
		||||
                                                current_api_path)) {
 | 
			
		||||
                  continue;
 | 
			
		||||
                }
 | 
			
		||||
@@ -347,12 +345,12 @@ auto encrypt_provider::get_file_list(api_file_list &list,
 | 
			
		||||
      for (const auto &dir_entry : utils::file::directory{path}.get_items()) {
 | 
			
		||||
        std::string api_path{};
 | 
			
		||||
        if (dir_entry->is_directory_item()) {
 | 
			
		||||
          process_directory_entry(*dir_entry.get(), cfg, api_path);
 | 
			
		||||
          process_directory_entry(*dir_entry, cfg, api_path);
 | 
			
		||||
          process_directory(dir_entry->get_path());
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (process_directory_entry(*dir_entry.get(), cfg, api_path)) {
 | 
			
		||||
        if (process_directory_entry(*dir_entry, cfg, api_path)) {
 | 
			
		||||
          list.emplace_back(create_api_file(
 | 
			
		||||
              api_path, dir_entry->is_directory_item(), dir_entry->get_path()));
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -67,7 +67,7 @@ auto sia_provider::check_version(std::string &required_version,
 | 
			
		||||
                                 std::string &returned_version) const -> bool {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  required_version = "2.0.0";
 | 
			
		||||
  required_version = RENTERD_MIN_VERSION;
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    curl::requests::http_get get{};
 | 
			
		||||
@@ -149,12 +149,58 @@ auto sia_provider::create_directory_impl(const std::string &api_path,
 | 
			
		||||
  return api_error::success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto sia_provider::create_directory_key(const std::string &api_path) const
 | 
			
		||||
    -> api_error {
 | 
			
		||||
  auto parent_api_path = utils::path::get_parent_api_path(api_path);
 | 
			
		||||
 | 
			
		||||
  json object_list;
 | 
			
		||||
  if (not get_object_list(parent_api_path, object_list)) {
 | 
			
		||||
    return api_error::comm_error;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (not object_list.contains("objects")) {
 | 
			
		||||
    return api_error::directory_not_found;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const auto &list = object_list.at("objects");
 | 
			
		||||
  if (std::ranges::find_if(list, [&api_path](auto &&entry) -> bool {
 | 
			
		||||
        return entry.at("key").template get<std::string>() == api_path + '/';
 | 
			
		||||
      }) == list.end()) {
 | 
			
		||||
    return api_error::directory_not_found;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  api_meta_map meta;
 | 
			
		||||
  return const_cast<sia_provider *>(this)->create_directory_impl(api_path,
 | 
			
		||||
                                                                 meta);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto sia_provider::ensure_directory_exists(const std::string &api_path) const
 | 
			
		||||
    -> api_error {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  bool exists{};
 | 
			
		||||
  auto res{is_directory(api_path, exists)};
 | 
			
		||||
  if (res != api_error::success) {
 | 
			
		||||
    utils::error::raise_api_path_error(function_name, api_path, res,
 | 
			
		||||
                                       "failed detect existing directory");
 | 
			
		||||
    return res;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (not exists) {
 | 
			
		||||
    utils::error::raise_api_path_error(function_name, api_path, res,
 | 
			
		||||
                                       "directory not found");
 | 
			
		||||
    return api_error::directory_not_found;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return api_error::success;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto sia_provider::get_directory_item_count(const std::string &api_path) const
 | 
			
		||||
    -> std::uint64_t {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    json object_list{};
 | 
			
		||||
    json object_list;
 | 
			
		||||
    if (not get_object_list(api_path, object_list)) {
 | 
			
		||||
      return 0U;
 | 
			
		||||
    }
 | 
			
		||||
@@ -192,41 +238,25 @@ auto sia_provider::get_directory_items_impl(const std::string &api_path,
 | 
			
		||||
    -> api_error {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  json object_list{};
 | 
			
		||||
  json object_list;
 | 
			
		||||
  if (not get_object_list(api_path, object_list)) {
 | 
			
		||||
    return api_error::comm_error;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (object_list.contains("objects")) {
 | 
			
		||||
    for (const auto &entry : object_list.at("objects")) {
 | 
			
		||||
      try {
 | 
			
		||||
        auto name{entry.at("key").get<std::string>()};
 | 
			
		||||
        auto entry_api_path{utils::path::create_api_path(name)};
 | 
			
		||||
 | 
			
		||||
        auto directory{utils::string::ends_with(name, "/")};
 | 
			
		||||
        if (directory && (entry_api_path == api_path)) {
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        api_file file{};
 | 
			
		||||
        api_meta_map meta{};
 | 
			
		||||
        if (get_item_meta(entry_api_path, meta) == api_error::item_not_found) {
 | 
			
		||||
          file = create_api_file(entry_api_path, "",
 | 
			
		||||
                                 directory ? 0U
 | 
			
		||||
                                           : entry["size"].get<std::uint64_t>(),
 | 
			
		||||
                                 get_last_modified(entry));
 | 
			
		||||
          get_api_item_added()(directory, file);
 | 
			
		||||
  iterate_objects(
 | 
			
		||||
      api_path, object_list,
 | 
			
		||||
      [&](auto &&entry_api_path, auto &&directory, auto &&entry) {
 | 
			
		||||
        api_meta_map meta;
 | 
			
		||||
        auto res{get_item_meta(entry_api_path, meta)};
 | 
			
		||||
        if (res != api_error::success) {
 | 
			
		||||
            utils::error::raise_api_path_error(function_name, entry_api_path,
 | 
			
		||||
                                               res, "failed to get item meta");
 | 
			
		||||
            continue;
 | 
			
		||||
          utils::error::raise_api_path_error(function_name, entry_api_path, res,
 | 
			
		||||
                                             "failed to get item meta");
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
        } else {
 | 
			
		||||
          file = create_api_file(
 | 
			
		||||
 | 
			
		||||
        auto file = create_api_file(
 | 
			
		||||
            entry_api_path,
 | 
			
		||||
              directory ? 0U : entry["size"].get<std::uint64_t>(), meta);
 | 
			
		||||
        }
 | 
			
		||||
            directory ? 0U : entry["size"].template get<std::uint64_t>(), meta);
 | 
			
		||||
 | 
			
		||||
        directory_item dir_item{};
 | 
			
		||||
        dir_item.api_parent = file.api_parent;
 | 
			
		||||
@@ -235,13 +265,7 @@ auto sia_provider::get_directory_items_impl(const std::string &api_path,
 | 
			
		||||
        dir_item.meta = meta;
 | 
			
		||||
        dir_item.size = file.file_size;
 | 
			
		||||
        list.emplace_back(std::move(dir_item));
 | 
			
		||||
      } catch (const std::exception &e) {
 | 
			
		||||
        utils::error::raise_api_path_error(function_name, api_path, e,
 | 
			
		||||
                                           "failed to process entry|" +
 | 
			
		||||
                                               entry.dump());
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
  return api_error::success;
 | 
			
		||||
}
 | 
			
		||||
@@ -290,73 +314,45 @@ auto sia_provider::get_file(const std::string &api_path, api_file &file) const
 | 
			
		||||
  return api_error::error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto sia_provider::get_file_list(api_file_list &list,
 | 
			
		||||
                                 std::string & /* marker */) const
 | 
			
		||||
auto sia_provider::get_file_list(api_file_list &list, std::string &marker) const
 | 
			
		||||
    -> api_error {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  using dir_func = std::function<api_error(std::string api_path)>;
 | 
			
		||||
  const dir_func get_files_in_dir = [&](std::string api_path) -> api_error {
 | 
			
		||||
  try {
 | 
			
		||||
      nlohmann::json object_list{};
 | 
			
		||||
      if (not get_object_list(api_path, object_list)) {
 | 
			
		||||
    json object_list;
 | 
			
		||||
    if (not get_object_list("", object_list, marker)) {
 | 
			
		||||
      return api_error::comm_error;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
      if (object_list.contains("objects")) {
 | 
			
		||||
        for (const auto &entry : object_list.at("objects")) {
 | 
			
		||||
          auto name{entry.at("key").get<std::string>()};
 | 
			
		||||
          auto entry_api_path{utils::path::create_api_path(name)};
 | 
			
		||||
 | 
			
		||||
          if (utils::string::ends_with(name, "/")) {
 | 
			
		||||
            if (entry_api_path == utils::path::create_api_path(api_path)) {
 | 
			
		||||
              continue;
 | 
			
		||||
    iterate_objects("/", object_list,
 | 
			
		||||
                    [&](auto &&entry_api_path, auto &&directory, auto &&entry) {
 | 
			
		||||
                      if (directory) {
 | 
			
		||||
                        return;
 | 
			
		||||
                      }
 | 
			
		||||
 | 
			
		||||
            api_meta_map meta{};
 | 
			
		||||
            if (get_item_meta(entry_api_path, meta) ==
 | 
			
		||||
                api_error::item_not_found) {
 | 
			
		||||
              auto dir{
 | 
			
		||||
                  create_api_file(entry_api_path, "", 0U,
 | 
			
		||||
                                  get_last_modified(entry)),
 | 
			
		||||
              };
 | 
			
		||||
              get_api_item_added()(true, dir);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            auto res{get_files_in_dir(entry_api_path)};
 | 
			
		||||
                      api_meta_map meta;
 | 
			
		||||
                      auto res{get_item_meta(entry_api_path, meta)};
 | 
			
		||||
                      if (res != api_error::success) {
 | 
			
		||||
              return res;
 | 
			
		||||
            }
 | 
			
		||||
            continue;
 | 
			
		||||
                        utils::error::raise_api_path_error(
 | 
			
		||||
                            function_name, entry_api_path, res,
 | 
			
		||||
                            "failed to get item meta");
 | 
			
		||||
                        return;
 | 
			
		||||
                      }
 | 
			
		||||
 | 
			
		||||
          api_file file{};
 | 
			
		||||
          api_meta_map meta{};
 | 
			
		||||
          if (get_item_meta(entry_api_path, meta) ==
 | 
			
		||||
              api_error::item_not_found) {
 | 
			
		||||
            file = create_api_file(entry_api_path, "",
 | 
			
		||||
                                   entry["size"].get<std::uint64_t>(),
 | 
			
		||||
                                   get_last_modified(entry));
 | 
			
		||||
            get_api_item_added()(false, file);
 | 
			
		||||
          } else {
 | 
			
		||||
            file = create_api_file(entry_api_path,
 | 
			
		||||
                                   entry["size"].get<std::uint64_t>(), meta);
 | 
			
		||||
          }
 | 
			
		||||
                      list.emplace_back(create_api_file(
 | 
			
		||||
                          entry_api_path,
 | 
			
		||||
                          entry["size"].template get<std::uint64_t>(), meta));
 | 
			
		||||
                    });
 | 
			
		||||
 | 
			
		||||
          list.emplace_back(std::move(file));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return api_error::success;
 | 
			
		||||
    marker = object_list.at("nextMarker").get<std::string>();
 | 
			
		||||
    return object_list.at("hasMore").get<bool>() ? api_error::more_data
 | 
			
		||||
                                                 : api_error::success;
 | 
			
		||||
  } catch (const std::exception &e) {
 | 
			
		||||
      utils::error::raise_api_path_error(function_name, api_path, e,
 | 
			
		||||
    utils::error::raise_api_path_error(function_name, "/", e,
 | 
			
		||||
                                       "failed to process directory");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return api_error::error;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return get_files_in_dir("");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto sia_provider::get_object_info(const std::string &api_path,
 | 
			
		||||
@@ -408,7 +404,9 @@ auto sia_provider::get_object_info(const std::string &api_path,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto sia_provider::get_object_list(const std::string &api_path,
 | 
			
		||||
                                   nlohmann::json &object_list) const -> bool {
 | 
			
		||||
                                   nlohmann::json &object_list,
 | 
			
		||||
                                   std::optional<std::string> marker) const
 | 
			
		||||
    -> bool {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
@@ -416,7 +414,12 @@ auto sia_provider::get_object_list(const std::string &api_path,
 | 
			
		||||
    get.allow_timeout = true;
 | 
			
		||||
    get.path = "/api/bus/objects" + api_path + "/";
 | 
			
		||||
    get.query["bucket"] = get_bucket(get_sia_config());
 | 
			
		||||
    if (marker.has_value()) {
 | 
			
		||||
      get.query["limit"] = "1000";
 | 
			
		||||
      get.query["marker"] = marker.value();
 | 
			
		||||
    } else {
 | 
			
		||||
      get.query["delimiter"] = "/";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string error_data;
 | 
			
		||||
    get.response_handler = [&error_data, &object_list](auto &&data,
 | 
			
		||||
@@ -513,6 +516,14 @@ auto sia_provider::is_directory(const std::string &api_path, bool &exists) const
 | 
			
		||||
    json file_data{};
 | 
			
		||||
    auto res{get_object_info(api_path + '/', file_data)};
 | 
			
		||||
    if (res == api_error::item_not_found) {
 | 
			
		||||
      if (create_directory_key(api_path) == api_error::success) {
 | 
			
		||||
        exists = true;
 | 
			
		||||
        return api_error::success;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (res == api_error::directory_not_found ||
 | 
			
		||||
        res == api_error::item_not_found) {
 | 
			
		||||
      return api_error::success;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -609,6 +620,52 @@ auto sia_provider::is_online() const -> bool {
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void sia_provider::iterate_objects(
 | 
			
		||||
    const std::string &api_path, const json &object_list,
 | 
			
		||||
    std::function<void(const std::string &, bool, json)> handle_entry) const {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  if (not object_list.contains("objects")) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (const auto &entry : object_list.at("objects")) {
 | 
			
		||||
    try {
 | 
			
		||||
      auto name{entry.at("key").get<std::string>()};
 | 
			
		||||
 | 
			
		||||
      auto directory{utils::string::ends_with(name, "/")};
 | 
			
		||||
      auto entry_api_path{utils::path::create_api_path(name)};
 | 
			
		||||
 | 
			
		||||
      {
 | 
			
		||||
        api_meta_map meta{};
 | 
			
		||||
        if (get_item_meta(entry_api_path, meta) == api_error::item_not_found) {
 | 
			
		||||
          auto file = create_api_file(
 | 
			
		||||
              entry_api_path, "",
 | 
			
		||||
              directory ? 0U : entry["size"].get<std::uint64_t>(),
 | 
			
		||||
              get_last_modified(entry));
 | 
			
		||||
          if (directory) {
 | 
			
		||||
            auto res{ensure_directory_exists(entry_api_path)};
 | 
			
		||||
            if (res != api_error::success) {
 | 
			
		||||
              utils::error::raise_api_path_error(
 | 
			
		||||
                  function_name, entry_api_path, res,
 | 
			
		||||
                  "failed detect existing directory");
 | 
			
		||||
              continue;
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          get_api_item_added()(directory, file);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      handle_entry(entry_api_path, directory, entry);
 | 
			
		||||
    } catch (const std::exception &e) {
 | 
			
		||||
      utils::error::raise_api_path_error(
 | 
			
		||||
          function_name, api_path, e,
 | 
			
		||||
          fmt::format("failed to process entry|{}", entry.dump()));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto sia_provider::read_file_bytes(const std::string &api_path,
 | 
			
		||||
                                   std::size_t size, std::uint64_t offset,
 | 
			
		||||
                                   data_buffer &buffer,
 | 
			
		||||
@@ -636,7 +693,6 @@ auto sia_provider::read_file_bytes(const std::string &api_path,
 | 
			
		||||
         ++idx) {
 | 
			
		||||
      long response_code{};
 | 
			
		||||
      const auto notify_retry = [&]() {
 | 
			
		||||
        fmt::println("{}", std::string(buffer.begin(), buffer.end()));
 | 
			
		||||
        if (response_code == 0) {
 | 
			
		||||
          utils::error::raise_api_path_error(
 | 
			
		||||
              function_name, api_path, api_error::comm_error,
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,6 @@
 | 
			
		||||
#include "rpc/client/client.hpp"
 | 
			
		||||
 | 
			
		||||
#include "types/repertory.hpp"
 | 
			
		||||
#include "utils/base64.hpp"
 | 
			
		||||
#include "utils/utils.hpp"
 | 
			
		||||
 | 
			
		||||
namespace repertory {
 | 
			
		||||
client::client(rpc_host_info host_info) : host_info_(std::move(host_info)) {}
 | 
			
		||||
 
 | 
			
		||||
@@ -28,64 +28,16 @@
 | 
			
		||||
#include "events/types/service_stop_begin.hpp"
 | 
			
		||||
#include "events/types/service_stop_end.hpp"
 | 
			
		||||
#include "events/types/unmount_requested.hpp"
 | 
			
		||||
#include "utils/base64.hpp"
 | 
			
		||||
#include "rpc/common.hpp"
 | 
			
		||||
#include "utils/error_utils.hpp"
 | 
			
		||||
#include "utils/string.hpp"
 | 
			
		||||
 | 
			
		||||
namespace repertory {
 | 
			
		||||
server::server(app_config &config) : config_(config) {}
 | 
			
		||||
 | 
			
		||||
auto server::check_authorization(const httplib::Request &req) -> bool {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  if (config_.get_api_auth().empty() || config_.get_api_user().empty()) {
 | 
			
		||||
    utils::error::raise_error(function_name,
 | 
			
		||||
                              "authorization user or password is not set");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto authorization = req.get_header_value("Authorization");
 | 
			
		||||
  if (authorization.empty()) {
 | 
			
		||||
    utils::error::raise_error(function_name, "Authorization header is not set");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto auth_parts = utils::string::split(authorization, ' ', true);
 | 
			
		||||
  if (auth_parts.empty()) {
 | 
			
		||||
    utils::error::raise_error(function_name, "Authorization header is empty");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto auth_type = auth_parts[0U];
 | 
			
		||||
  if (auth_type != "Basic") {
 | 
			
		||||
    utils::error::raise_error(function_name, "Authorization is not Basic");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto data = macaron::Base64::Decode(authorization.substr(6U));
 | 
			
		||||
  auto auth_str = std::string(data.begin(), data.end());
 | 
			
		||||
 | 
			
		||||
  auto auth = utils::string::split(auth_str, ':', false);
 | 
			
		||||
  if (auth.size() < 2U) {
 | 
			
		||||
    utils::error::raise_error(function_name, "Authorization is not valid");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto user = auth.at(0U);
 | 
			
		||||
  auth.erase(auth.begin());
 | 
			
		||||
 | 
			
		||||
  auto pwd = utils::string::join(auth, ':');
 | 
			
		||||
  if ((user != config_.get_api_user()) || (pwd != config_.get_api_auth())) {
 | 
			
		||||
    utils::error::raise_error(function_name, "Authorization failed");
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void server::handle_get_config(const httplib::Request & /*req*/,
 | 
			
		||||
                               httplib::Response &res) {
 | 
			
		||||
  auto data = config_.get_json();
 | 
			
		||||
  clean_json_config(config_.get_provider_type(), data);
 | 
			
		||||
  res.set_content(data.dump(), "application/json");
 | 
			
		||||
  res.status = http_error_codes::ok;
 | 
			
		||||
}
 | 
			
		||||
@@ -93,7 +45,10 @@ void server::handle_get_config(const httplib::Request & /*req*/,
 | 
			
		||||
void server::handle_get_config_value_by_name(const httplib::Request &req,
 | 
			
		||||
                                             httplib::Response &res) {
 | 
			
		||||
  auto name = req.get_param_value("name");
 | 
			
		||||
  auto data = json({{"value", config_.get_value_by_name(name)}});
 | 
			
		||||
  auto data = json({{
 | 
			
		||||
      "value",
 | 
			
		||||
      clean_json_value(name, config_.get_value_by_name(name)),
 | 
			
		||||
  }});
 | 
			
		||||
  res.set_content(data.dump(), "application/json");
 | 
			
		||||
  res.status = http_error_codes::ok;
 | 
			
		||||
}
 | 
			
		||||
@@ -103,14 +58,19 @@ void server::handle_set_config_value_by_name(const httplib::Request &req,
 | 
			
		||||
  auto name = req.get_param_value("name");
 | 
			
		||||
  auto value = req.get_param_value("value");
 | 
			
		||||
 | 
			
		||||
  json data = {{"value", config_.set_value_by_name(name, value)}};
 | 
			
		||||
  json data = {{
 | 
			
		||||
      "value",
 | 
			
		||||
      clean_json_value(name, config_.set_value_by_name(name, value)),
 | 
			
		||||
  }};
 | 
			
		||||
  res.set_content(data.dump(), "application/json");
 | 
			
		||||
  res.status = http_error_codes::ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void server::handle_unmount(const httplib::Request & /*req*/,
 | 
			
		||||
                            httplib::Response &res) {
 | 
			
		||||
  event_system::instance().raise<unmount_requested>();
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  event_system::instance().raise<unmount_requested>(function_name);
 | 
			
		||||
  res.status = http_error_codes::ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -173,7 +133,7 @@ void server::start() {
 | 
			
		||||
 | 
			
		||||
  server_->set_pre_routing_handler(
 | 
			
		||||
      [this](auto &&req, auto &&res) -> httplib::Server::HandlerResponse {
 | 
			
		||||
        if (check_authorization(req)) {
 | 
			
		||||
        if (rpc::check_authorization(config_, req)) {
 | 
			
		||||
          return httplib::Server::HandlerResponse::Unhandled;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -183,8 +143,21 @@ void server::start() {
 | 
			
		||||
 | 
			
		||||
  initialize(*server_);
 | 
			
		||||
 | 
			
		||||
  server_thread_ = std::make_unique<std::thread>(
 | 
			
		||||
      [this]() { server_->listen("127.0.0.1", config_.get_api_port()); });
 | 
			
		||||
  server_thread_ = std::make_unique<std::thread>([this]() {
 | 
			
		||||
    server_->set_socket_options([](auto &&sock) {
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
      int enable{1};
 | 
			
		||||
      setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
 | 
			
		||||
                 reinterpret_cast<const char *>(&enable), sizeof(enable));
 | 
			
		||||
#else  //  !defined(_WIN32)
 | 
			
		||||
      linger opt{1, 0};
 | 
			
		||||
      setsockopt(sock, SOL_SOCKET, SO_LINGER,
 | 
			
		||||
                 reinterpret_cast<const char *>(&opt), sizeof(opt));
 | 
			
		||||
#endif // defined(_WIN32)
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    server_->listen("127.0.0.1", config_.get_api_port());
 | 
			
		||||
  });
 | 
			
		||||
  event_system::instance().raise<service_start_end>(function_name, "server");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,10 +21,56 @@
 | 
			
		||||
*/
 | 
			
		||||
#include "types/repertory.hpp"
 | 
			
		||||
 | 
			
		||||
#include "app_config.hpp"
 | 
			
		||||
#include "types/startup_exception.hpp"
 | 
			
		||||
#include "utils/string.hpp"
 | 
			
		||||
 | 
			
		||||
namespace repertory {
 | 
			
		||||
void clean_json_config(provider_type prov, nlohmann::json &data) {
 | 
			
		||||
  data[JSON_API_PASSWORD] = "";
 | 
			
		||||
 | 
			
		||||
  switch (prov) {
 | 
			
		||||
  case provider_type::encrypt:
 | 
			
		||||
    data[JSON_ENCRYPT_CONFIG][JSON_ENCRYPTION_TOKEN] = "";
 | 
			
		||||
    data[JSON_REMOTE_MOUNT][JSON_ENCRYPTION_TOKEN] = "";
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case provider_type::remote:
 | 
			
		||||
    data[JSON_REMOTE_CONFIG][JSON_ENCRYPTION_TOKEN] = "";
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case provider_type::s3:
 | 
			
		||||
    data[JSON_REMOTE_MOUNT][JSON_ENCRYPTION_TOKEN] = "";
 | 
			
		||||
    data[JSON_S3_CONFIG][JSON_ENCRYPTION_TOKEN] = "";
 | 
			
		||||
    data[JSON_S3_CONFIG][JSON_SECRET_KEY] = "";
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case provider_type::sia:
 | 
			
		||||
    data[JSON_REMOTE_MOUNT][JSON_ENCRYPTION_TOKEN] = "";
 | 
			
		||||
    data[JSON_HOST_CONFIG][JSON_API_PASSWORD] = "";
 | 
			
		||||
 | 
			
		||||
    break;
 | 
			
		||||
  default:
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto clean_json_value(std::string_view name, std::string_view data)
 | 
			
		||||
    -> std::string {
 | 
			
		||||
  if (name ==
 | 
			
		||||
          fmt::format("{}.{}", JSON_ENCRYPT_CONFIG, JSON_ENCRYPTION_TOKEN) ||
 | 
			
		||||
      name == fmt::format("{}.{}", JSON_HOST_CONFIG, JSON_API_PASSWORD) ||
 | 
			
		||||
      name == fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_ENCRYPTION_TOKEN) ||
 | 
			
		||||
      name == fmt::format("{}.{}", JSON_REMOTE_MOUNT, JSON_ENCRYPTION_TOKEN) ||
 | 
			
		||||
      name == fmt::format("{}.{}", JSON_S3_CONFIG, JSON_ENCRYPTION_TOKEN) ||
 | 
			
		||||
      name == fmt::format("{}.{}", JSON_S3_CONFIG, JSON_SECRET_KEY) ||
 | 
			
		||||
      name == JSON_API_PASSWORD) {
 | 
			
		||||
    return "";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return std::string{data};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto database_type_from_string(std::string type, database_type default_type)
 | 
			
		||||
    -> database_type {
 | 
			
		||||
  type = utils::string::to_lower(utils::string::trim(type));
 | 
			
		||||
@@ -191,4 +237,34 @@ auto api_error_to_string(const api_error &error) -> const std::string & {
 | 
			
		||||
 | 
			
		||||
  return LOOKUP.at(error);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto provider_type_from_string(std::string_view type,
 | 
			
		||||
                               provider_type default_type) -> provider_type {
 | 
			
		||||
  auto type_lower = utils::string::to_lower(std::string{type});
 | 
			
		||||
  if (type_lower == "encrypt") {
 | 
			
		||||
    return provider_type::encrypt;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (type_lower == "remote") {
 | 
			
		||||
    return provider_type::remote;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (type_lower == "s3") {
 | 
			
		||||
    return provider_type::s3;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (type_lower == "sia") {
 | 
			
		||||
    return provider_type::sia;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (type_lower == "unknown") {
 | 
			
		||||
    return provider_type::unknown;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return default_type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto provider_type_to_string(provider_type type) -> std::string {
 | 
			
		||||
  return app_config::get_provider_name(type);
 | 
			
		||||
}
 | 
			
		||||
} // namespace repertory
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,7 @@ void get_api_authentication_data(std::string &user, std::string &password,
 | 
			
		||||
 | 
			
		||||
  if (success) {
 | 
			
		||||
    if (user.empty() && password.empty()) {
 | 
			
		||||
      password = data[JSON_API_AUTH].get<std::string>();
 | 
			
		||||
      password = data[JSON_API_PASSWORD].get<std::string>();
 | 
			
		||||
      user = data[JSON_API_USER].get<std::string>();
 | 
			
		||||
    }
 | 
			
		||||
    port = data[JSON_API_PORT].get<std::uint16_t>();
 | 
			
		||||
 
 | 
			
		||||
@@ -22,13 +22,23 @@
 | 
			
		||||
#include "utils/error_utils.hpp"
 | 
			
		||||
 | 
			
		||||
#include "events/event_system.hpp"
 | 
			
		||||
#include "events/types/debug_log.hpp"
 | 
			
		||||
#include "events/types/info_log.hpp"
 | 
			
		||||
#include "events/types/repertory_exception.hpp"
 | 
			
		||||
#include "events/types/trace_log.hpp"
 | 
			
		||||
#include "events/types/warn_log.hpp"
 | 
			
		||||
#include "types/repertory.hpp"
 | 
			
		||||
#include "utils/error.hpp"
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
struct repertory_exception_handler final
 | 
			
		||||
    : repertory::utils::error::i_exception_handler {
 | 
			
		||||
  void handle_debug(std::string_view function_name,
 | 
			
		||||
                    std::string_view msg) const override {
 | 
			
		||||
    repertory::event_system::instance().raise<repertory::debug_log>(
 | 
			
		||||
        function_name, std::string{msg});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void handle_error(std::string_view function_name,
 | 
			
		||||
                    std::string_view msg) const override {
 | 
			
		||||
    repertory::utils::error::raise_error(function_name, msg);
 | 
			
		||||
@@ -42,13 +52,33 @@ struct repertory_exception_handler final
 | 
			
		||||
                        const std::exception &ex) const override {
 | 
			
		||||
    repertory::utils::error::raise_error(function_name, ex);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void handle_info(std::string_view function_name,
 | 
			
		||||
                   std::string_view msg) const override {
 | 
			
		||||
    repertory::event_system::instance().raise<repertory::info_log>(
 | 
			
		||||
        function_name, std::string{msg});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void handle_trace(std::string_view function_name,
 | 
			
		||||
                    std::string_view msg) const override {
 | 
			
		||||
    repertory::event_system::instance().raise<repertory::trace_log>(
 | 
			
		||||
        function_name, std::string{msg});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void handle_warn(std::string_view function_name,
 | 
			
		||||
                   std::string_view msg) const override {
 | 
			
		||||
    repertory::event_system::instance().raise<repertory::warn_log>(
 | 
			
		||||
        function_name, std::string{msg});
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<repertory_exception_handler> handler{([]() -> auto * {
 | 
			
		||||
  auto *ptr = new repertory_exception_handler{};
 | 
			
		||||
  repertory::utils::error::set_exception_handler(ptr);
 | 
			
		||||
const auto repertory_handler{
 | 
			
		||||
    ([]() -> auto {
 | 
			
		||||
      auto ptr = std::make_unique<repertory_exception_handler>();
 | 
			
		||||
      repertory::utils::error::set_exception_handler(ptr.get());
 | 
			
		||||
      return ptr;
 | 
			
		||||
})()};
 | 
			
		||||
    })(),
 | 
			
		||||
};
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
namespace repertory::utils::error {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										31
									
								
								repertory/librepertory/src/utils/platform.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								repertory/librepertory/src/utils/platform.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
/*
 | 
			
		||||
  Copyright <2018-2025> <scott.e.graves@protonmail.com>
 | 
			
		||||
 | 
			
		||||
  Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
  of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
  in the Software without restriction, including without limitation the rights
 | 
			
		||||
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
  copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
  furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
  The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
  copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
  SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
#include "platform/platform.hpp"
 | 
			
		||||
 | 
			
		||||
#include "app_config.hpp"
 | 
			
		||||
 | 
			
		||||
namespace repertory {
 | 
			
		||||
auto create_lock_id(provider_type prov, std::string_view unique_id)->std::string {
 | 
			
		||||
  return fmt::format("{}_{}_{}", REPERTORY_DATA_NAME,
 | 
			
		||||
                     app_config::get_provider_name(prov), unique_id);
 | 
			
		||||
}
 | 
			
		||||
} // namespace repertory
 | 
			
		||||
@@ -36,6 +36,7 @@
 | 
			
		||||
#include "cli/pinned_status.hpp"
 | 
			
		||||
#include "cli/set.hpp"
 | 
			
		||||
#include "cli/status.hpp"
 | 
			
		||||
#include "cli/ui.hpp"
 | 
			
		||||
#include "cli/unmount.hpp"
 | 
			
		||||
#include "cli/unpin_file.hpp"
 | 
			
		||||
#include "utils/cli_utils.hpp"
 | 
			
		||||
@@ -70,6 +71,7 @@ static const std::unordered_map<utils::cli::option, action, option_hasher>
 | 
			
		||||
         cli::actions::pinned_status},
 | 
			
		||||
        {utils::cli::options::set_option, cli::actions::set},
 | 
			
		||||
        {utils::cli::options::status_option, cli::actions::status},
 | 
			
		||||
        {utils::cli::options::ui_option, cli::actions::ui},
 | 
			
		||||
        {utils::cli::options::unmount_option, cli::actions::unmount},
 | 
			
		||||
        {utils::cli::options::unpin_file_option, cli::actions::unpin_file},
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -36,9 +36,8 @@ template <typename drive> inline void help(std::vector<const char *> args) {
 | 
			
		||||
  std::cout << "    -di,--drive_information           Display mounted drive "
 | 
			
		||||
               "information"
 | 
			
		||||
            << std::endl;
 | 
			
		||||
  std::cout
 | 
			
		||||
      << "    -na,--name                        Unique name for S3 or Sia "
 | 
			
		||||
         "instance [Required]"
 | 
			
		||||
  std::cout << "    -na,--name                        Unique configuration "
 | 
			
		||||
               "name [Required for Encrypt, S3 and Sia]"
 | 
			
		||||
            << std::endl;
 | 
			
		||||
  std::cout << "    -s3,--s3                          Enables S3 mode"
 | 
			
		||||
            << std::endl;
 | 
			
		||||
@@ -79,6 +78,12 @@ template <typename drive> inline void help(std::vector<const char *> args) {
 | 
			
		||||
            << std::endl;
 | 
			
		||||
  std::cout << "    -status                           Display mount status"
 | 
			
		||||
            << std::endl;
 | 
			
		||||
  std::cout
 | 
			
		||||
      << "    -ui,--ui                          Run embedded management UI"
 | 
			
		||||
      << std::endl;
 | 
			
		||||
  std::cout << "    -up,--ui_port                     Custom port for embedded "
 | 
			
		||||
               "management UI"
 | 
			
		||||
            << std::endl;
 | 
			
		||||
  std::cout << "    -unmount,--unmount                Unmount and shutdown"
 | 
			
		||||
            << std::endl;
 | 
			
		||||
  std::cout << "    -uf,--unpin_file [API path]       Unpin a file from cache "
 | 
			
		||||
 
 | 
			
		||||
@@ -28,14 +28,13 @@
 | 
			
		||||
#include "providers/provider.hpp"
 | 
			
		||||
#include "types/repertory.hpp"
 | 
			
		||||
#include "utils/cli_utils.hpp"
 | 
			
		||||
#include "utils/com_init_wrapper.hpp"
 | 
			
		||||
#include "utils/file_utils.hpp"
 | 
			
		||||
#include "utils/string.hpp"
 | 
			
		||||
#include "utils/file.hpp"
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
#include "drives/winfsp/remotewinfsp/remote_client.hpp"
 | 
			
		||||
#include "drives/winfsp/remotewinfsp/remote_winfsp_drive.hpp"
 | 
			
		||||
#include "drives/winfsp/winfsp_drive.hpp"
 | 
			
		||||
#include "utils/com_init_wrapper.hpp"
 | 
			
		||||
 | 
			
		||||
using repertory_drive = repertory::winfsp_drive;
 | 
			
		||||
using remote_client = repertory::remote_winfsp::remote_client;
 | 
			
		||||
@@ -57,24 +56,36 @@ namespace repertory::cli::actions {
 | 
			
		||||
mount(std::vector<const char *> args, std::string data_directory,
 | 
			
		||||
      int &mount_result, provider_type prov, const std::string &remote_host,
 | 
			
		||||
      std::uint16_t remote_port, const std::string &unique_id) -> exit_code {
 | 
			
		||||
  auto ret = exit_code::success;
 | 
			
		||||
  lock_data global_lock(provider_type::unknown, "global");
 | 
			
		||||
  {
 | 
			
		||||
    auto lock_result = global_lock.grab_lock(100U);
 | 
			
		||||
    if (lock_result != lock_result::success) {
 | 
			
		||||
      std::cerr << "FATAL: Unable to get global lock" << std::endl;
 | 
			
		||||
      return exit_code::lock_failed;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  lock_data lock(prov, unique_id);
 | 
			
		||||
  const auto res = lock.grab_lock();
 | 
			
		||||
  if (res == lock_result::locked) {
 | 
			
		||||
    ret = exit_code::mount_active;
 | 
			
		||||
  auto lock_result = lock.grab_lock();
 | 
			
		||||
  if (lock_result == lock_result::locked) {
 | 
			
		||||
    std::cerr << app_config::get_provider_display_name(prov)
 | 
			
		||||
              << " mount is already active" << std::endl;
 | 
			
		||||
  } else if (res == lock_result::success) {
 | 
			
		||||
    const auto generate_config = utils::cli::has_option(
 | 
			
		||||
        args, utils::cli::options::generate_config_option);
 | 
			
		||||
    if (generate_config) {
 | 
			
		||||
    return exit_code::mount_active;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (lock_result != lock_result::success) {
 | 
			
		||||
    std::cerr << "FATAL: Unable to get provider lock" << std::endl;
 | 
			
		||||
    return exit_code::lock_failed;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (utils::cli::has_option(args,
 | 
			
		||||
                             utils::cli::options::generate_config_option)) {
 | 
			
		||||
    app_config config(prov, data_directory);
 | 
			
		||||
    if (prov == provider_type::remote) {
 | 
			
		||||
        auto cfg = config.get_remote_config();
 | 
			
		||||
        cfg.host_name_or_ip = remote_host;
 | 
			
		||||
        cfg.api_port = remote_port;
 | 
			
		||||
        config.set_remote_config(cfg);
 | 
			
		||||
      auto remote_config = config.get_remote_config();
 | 
			
		||||
      remote_config.host_name_or_ip = remote_host;
 | 
			
		||||
      remote_config.api_port = remote_port;
 | 
			
		||||
      config.set_remote_config(remote_config);
 | 
			
		||||
    } else if (prov == provider_type::sia &&
 | 
			
		||||
               config.get_sia_config().bucket.empty()) {
 | 
			
		||||
      [[maybe_unused]] auto bucket =
 | 
			
		||||
@@ -84,103 +95,104 @@ mount(std::vector<const char *> args, std::string data_directory,
 | 
			
		||||
    std::cout << "Generated " << app_config::get_provider_display_name(prov)
 | 
			
		||||
              << " Configuration" << std::endl;
 | 
			
		||||
    std::cout << config.get_config_file_path() << std::endl;
 | 
			
		||||
      ret = utils::file::file(config.get_config_file_path()).exists()
 | 
			
		||||
    return utils::file::file(config.get_config_file_path()).exists()
 | 
			
		||||
               ? exit_code::success
 | 
			
		||||
               : exit_code::file_creation_failed;
 | 
			
		||||
    } else {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
  if (utils::cli::has_option(args, utils::cli::options::hidden_option)) {
 | 
			
		||||
    ::ShowWindow(::GetConsoleWindow(), SW_HIDE);
 | 
			
		||||
  }
 | 
			
		||||
#endif // defined(_WIN32)
 | 
			
		||||
      auto drive_args =
 | 
			
		||||
          utils::cli::parse_drive_options(args, prov, data_directory);
 | 
			
		||||
 | 
			
		||||
  auto drive_args = utils::cli::parse_drive_options(args, prov, data_directory);
 | 
			
		||||
  app_config config(prov, data_directory);
 | 
			
		||||
  {
 | 
			
		||||
    std::uint16_t port{};
 | 
			
		||||
    if (not utils::get_next_available_port(config.get_api_port(), port)) {
 | 
			
		||||
      std::cerr << "FATAL: Unable to get available port" << std::endl;
 | 
			
		||||
      return exit_code::startup_exception;
 | 
			
		||||
    }
 | 
			
		||||
    config.set_api_port(port);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
      if (config.get_enable_mount_manager() &&
 | 
			
		||||
          not utils::is_process_elevated()) {
 | 
			
		||||
        utils::com_init_wrapper cw;
 | 
			
		||||
  if (config.get_enable_mount_manager() && not utils::is_process_elevated()) {
 | 
			
		||||
    utils::com_init_wrapper wrapper;
 | 
			
		||||
    if (not lock.set_mount_state(true, "elevating", -1)) {
 | 
			
		||||
      std::cerr << "failed to set mount state" << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
    lock.release();
 | 
			
		||||
    global_lock.release();
 | 
			
		||||
 | 
			
		||||
    mount_result = utils::run_process_elevated(args);
 | 
			
		||||
        lock_data lock2(prov, unique_id);
 | 
			
		||||
        if (lock2.grab_lock() == lock_result::success) {
 | 
			
		||||
          if (not lock2.set_mount_state(false, "", -1)) {
 | 
			
		||||
    lock_data prov_lock(prov, unique_id);
 | 
			
		||||
    if (prov_lock.grab_lock() == lock_result::success) {
 | 
			
		||||
      if (not prov_lock.set_mount_state(false, "", -1)) {
 | 
			
		||||
        std::cerr << "failed to set mount state" << std::endl;
 | 
			
		||||
      }
 | 
			
		||||
          lock2.release();
 | 
			
		||||
      prov_lock.release();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return exit_code::mount_result;
 | 
			
		||||
  }
 | 
			
		||||
#endif // defined(_WIN32)
 | 
			
		||||
      std::cout << "Initializing "
 | 
			
		||||
                << app_config::get_provider_display_name(prov)
 | 
			
		||||
 | 
			
		||||
  std::cout << "Initializing " << app_config::get_provider_display_name(prov)
 | 
			
		||||
            << (unique_id.empty() ? ""
 | 
			
		||||
                    : (prov == provider_type::s3 || prov == provider_type::sia)
 | 
			
		||||
                        ? " [" + unique_id + ']'
 | 
			
		||||
                        : " [" + remote_host + ':' +
 | 
			
		||||
                              std::to_string(remote_port) + ']')
 | 
			
		||||
                : (prov == provider_type::remote)
 | 
			
		||||
                    ? " [" + remote_host + ':' + std::to_string(remote_port) +
 | 
			
		||||
                          ']'
 | 
			
		||||
                    : " [" + unique_id + ']')
 | 
			
		||||
            << " Drive" << std::endl;
 | 
			
		||||
  if (prov == provider_type::remote) {
 | 
			
		||||
        std::uint16_t port{0U};
 | 
			
		||||
        if (utils::get_next_available_port(config.get_api_port(), port)) {
 | 
			
		||||
          auto cfg = config.get_remote_config();
 | 
			
		||||
          cfg.host_name_or_ip = remote_host;
 | 
			
		||||
          cfg.api_port = remote_port;
 | 
			
		||||
          config.set_remote_config(cfg);
 | 
			
		||||
          config.set_api_port(port);
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      auto remote_cfg = config.get_remote_config();
 | 
			
		||||
      remote_cfg.host_name_or_ip = remote_host;
 | 
			
		||||
      remote_cfg.api_port = remote_port;
 | 
			
		||||
      config.set_remote_config(remote_cfg);
 | 
			
		||||
 | 
			
		||||
      remote_drive drive(
 | 
			
		||||
          config,
 | 
			
		||||
          [&config]() -> std::unique_ptr<remote_instance> {
 | 
			
		||||
                  return std::unique_ptr<remote_instance>(
 | 
			
		||||
                      new remote_client(config));
 | 
			
		||||
            return std::unique_ptr<remote_instance>(new remote_client(config));
 | 
			
		||||
          },
 | 
			
		||||
          lock);
 | 
			
		||||
      if (not lock.set_mount_state(true, "", -1)) {
 | 
			
		||||
        std::cerr << "failed to set mount state" << std::endl;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      global_lock.release();
 | 
			
		||||
      mount_result = drive.mount(drive_args);
 | 
			
		||||
            ret = exit_code::mount_result;
 | 
			
		||||
      return exit_code::mount_result;
 | 
			
		||||
    } catch (const std::exception &e) {
 | 
			
		||||
      std::cerr << "FATAL: " << e.what() << std::endl;
 | 
			
		||||
            ret = exit_code::startup_exception;
 | 
			
		||||
    }
 | 
			
		||||
        } else {
 | 
			
		||||
          std::cerr << "FATAL: Unable to get available port" << std::endl;
 | 
			
		||||
          ret = exit_code::startup_exception;
 | 
			
		||||
 | 
			
		||||
    return exit_code::startup_exception;
 | 
			
		||||
  }
 | 
			
		||||
      } else {
 | 
			
		||||
        if (prov == provider_type::sia &&
 | 
			
		||||
            config.get_sia_config().bucket.empty()) {
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    if (prov == provider_type::sia && config.get_sia_config().bucket.empty()) {
 | 
			
		||||
      [[maybe_unused]] auto bucket =
 | 
			
		||||
          config.set_value_by_name("SiaConfig.Bucket", unique_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
    auto provider = create_provider(prov, config);
 | 
			
		||||
    repertory_drive drive(config, lock, *provider);
 | 
			
		||||
    if (not lock.set_mount_state(true, "", -1)) {
 | 
			
		||||
      std::cerr << "failed to set mount state" << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    global_lock.release();
 | 
			
		||||
    mount_result = drive.mount(drive_args);
 | 
			
		||||
          ret = exit_code::mount_result;
 | 
			
		||||
    return exit_code::mount_result;
 | 
			
		||||
  } catch (const std::exception &e) {
 | 
			
		||||
    std::cerr << "FATAL: " << e.what() << std::endl;
 | 
			
		||||
          ret = exit_code::startup_exception;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    ret = exit_code::lock_failed;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return ret;
 | 
			
		||||
  return exit_code::startup_exception;
 | 
			
		||||
}
 | 
			
		||||
} // namespace repertory::cli::actions
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										61
									
								
								repertory/repertory/include/cli/ui.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								repertory/repertory/include/cli/ui.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,61 @@
 | 
			
		||||
/*
 | 
			
		||||
  Copyright <2018-2025> <scott.e.graves@protonmail.com>
 | 
			
		||||
 | 
			
		||||
  Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
  of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
  in the Software without restriction, including without limitation the rights
 | 
			
		||||
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
  copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
  furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
  The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
  copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
  SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
#ifndef REPERTORY_INCLUDE_CLI_UI_HPP_
 | 
			
		||||
#define REPERTORY_INCLUDE_CLI_UI_HPP_
 | 
			
		||||
 | 
			
		||||
#include "types/repertory.hpp"
 | 
			
		||||
#include "ui/handlers.hpp"
 | 
			
		||||
#include "ui/mgmt_app_config.hpp"
 | 
			
		||||
#include "utils/cli_utils.hpp"
 | 
			
		||||
#include "utils/file.hpp"
 | 
			
		||||
#include "utils/string.hpp"
 | 
			
		||||
 | 
			
		||||
namespace repertory::cli::actions {
 | 
			
		||||
[[nodiscard]] inline auto
 | 
			
		||||
ui(std::vector<const char *> args, const std::string & /*data_directory*/,
 | 
			
		||||
   const provider_type & /* prov */, const std::string & /* unique_id */,
 | 
			
		||||
   std::string /* user */, std::string /* password */) -> exit_code {
 | 
			
		||||
 | 
			
		||||
  ui::mgmt_app_config config{};
 | 
			
		||||
 | 
			
		||||
  std::string data;
 | 
			
		||||
  auto res = utils::cli::parse_string_option(
 | 
			
		||||
      args, utils::cli::options::ui_port_option, data);
 | 
			
		||||
  if (res == exit_code::success && not data.empty()) {
 | 
			
		||||
    config.set_api_port(utils::string::to_uint16(data));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (not utils::file::change_to_process_directory()) {
 | 
			
		||||
    return exit_code::ui_mount_failed;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  httplib::Server server;
 | 
			
		||||
  if (not server.set_mount_point("/ui", "./web")) {
 | 
			
		||||
    return exit_code::ui_mount_failed;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ui::handlers handlers(&config, &server);
 | 
			
		||||
  return exit_code::success;
 | 
			
		||||
}
 | 
			
		||||
} // namespace repertory::cli::actions
 | 
			
		||||
 | 
			
		||||
#endif // REPERTORY_INCLUDE_CLI_UI_HPP_
 | 
			
		||||
							
								
								
									
										120
									
								
								repertory/repertory/include/ui/handlers.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								repertory/repertory/include/ui/handlers.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,120 @@
 | 
			
		||||
/*
 | 
			
		||||
  Copyright <2018-2025> <scott.e.graves@protonmail.com>
 | 
			
		||||
 | 
			
		||||
  Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
  of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
  in the Software without restriction, including without limitation the rights
 | 
			
		||||
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
  copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
  furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
  The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
  copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
  SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
#ifndef REPERTORY_INCLUDE_UI_HANDLERS_HPP_
 | 
			
		||||
#define REPERTORY_INCLUDE_UI_HANDLERS_HPP_
 | 
			
		||||
 | 
			
		||||
#include "events/consumers/console_consumer.hpp"
 | 
			
		||||
#include "utils/common.hpp"
 | 
			
		||||
 | 
			
		||||
namespace repertory::ui {
 | 
			
		||||
class mgmt_app_config;
 | 
			
		||||
 | 
			
		||||
class handlers final {
 | 
			
		||||
private:
 | 
			
		||||
  static constexpr const auto nonce_length{128U};
 | 
			
		||||
  static constexpr const auto nonce_timeout{15U};
 | 
			
		||||
 | 
			
		||||
  struct nonce_data final {
 | 
			
		||||
    std::chrono::system_clock::time_point creation{
 | 
			
		||||
        std::chrono::system_clock::now(),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    std::string nonce{
 | 
			
		||||
        utils::generate_random_string(nonce_length),
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  handlers(mgmt_app_config *config, httplib::Server *server);
 | 
			
		||||
 | 
			
		||||
  handlers() = delete;
 | 
			
		||||
  handlers(const handlers &) = delete;
 | 
			
		||||
  handlers(handlers &&) = delete;
 | 
			
		||||
 | 
			
		||||
  ~handlers();
 | 
			
		||||
 | 
			
		||||
  auto operator=(const handlers &) -> handlers & = delete;
 | 
			
		||||
  auto operator=(handlers &&) -> handlers & = delete;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  mgmt_app_config *config_;
 | 
			
		||||
  std::string repertory_binary_;
 | 
			
		||||
  httplib::Server *server_;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  console_consumer console;
 | 
			
		||||
  mutable std::mutex mtx_;
 | 
			
		||||
  mutable std::unordered_map<std::string, std::recursive_mutex> mtx_lookup_;
 | 
			
		||||
  std::mutex nonce_mtx_;
 | 
			
		||||
  std::unordered_map<std::string, nonce_data> nonce_lookup_;
 | 
			
		||||
  std::condition_variable nonce_notify_;
 | 
			
		||||
  std::unique_ptr<std::thread> nonce_thread_;
 | 
			
		||||
  stop_type stop_requested{false};
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  [[nodiscard]] auto data_directory_exists(provider_type prov,
 | 
			
		||||
                                           std::string_view name) const -> bool;
 | 
			
		||||
 | 
			
		||||
  static void handle_get_available_locations(httplib::Response &res);
 | 
			
		||||
 | 
			
		||||
  void handle_get_mount(const httplib::Request &req,
 | 
			
		||||
                        httplib::Response &res) const;
 | 
			
		||||
 | 
			
		||||
  static void handle_get_mount_list(httplib::Response &res);
 | 
			
		||||
 | 
			
		||||
  void handle_get_mount_location(const httplib::Request &req,
 | 
			
		||||
                                 httplib::Response &res) const;
 | 
			
		||||
 | 
			
		||||
  void handle_get_mount_status(const httplib::Request &req,
 | 
			
		||||
                               httplib::Response &res) const;
 | 
			
		||||
 | 
			
		||||
  void handle_get_nonce(httplib::Response &res);
 | 
			
		||||
 | 
			
		||||
  void handle_get_settings(httplib::Response &res) const;
 | 
			
		||||
 | 
			
		||||
  void handle_post_add_mount(const httplib::Request &req,
 | 
			
		||||
                             httplib::Response &res) const;
 | 
			
		||||
 | 
			
		||||
  void handle_post_mount(const httplib::Request &req, httplib::Response &res);
 | 
			
		||||
 | 
			
		||||
  void handle_put_mount_location(const httplib::Request &req,
 | 
			
		||||
                                 httplib::Response &res) const;
 | 
			
		||||
 | 
			
		||||
  void handle_put_set_value_by_name(const httplib::Request &req,
 | 
			
		||||
                                    httplib::Response &res) const;
 | 
			
		||||
 | 
			
		||||
  void handle_put_settings(const httplib::Request &req,
 | 
			
		||||
                           httplib::Response &res) const;
 | 
			
		||||
 | 
			
		||||
  auto launch_process(provider_type prov, std::string_view name,
 | 
			
		||||
                      std::vector<std::string> args,
 | 
			
		||||
                      bool background = false) const
 | 
			
		||||
      -> std::vector<std::string>;
 | 
			
		||||
 | 
			
		||||
  void removed_expired_nonces();
 | 
			
		||||
 | 
			
		||||
  void set_key_value(provider_type prov, std::string_view name,
 | 
			
		||||
                     std::string_view key, std::string_view value) const;
 | 
			
		||||
};
 | 
			
		||||
} // namespace repertory::ui
 | 
			
		||||
 | 
			
		||||
#endif // REPERTORY_INCLUDE_UI_HANDLERS_HPP_
 | 
			
		||||
							
								
								
									
										70
									
								
								repertory/repertory/include/ui/mgmt_app_config.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								repertory/repertory/include/ui/mgmt_app_config.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,70 @@
 | 
			
		||||
/*
 | 
			
		||||
  Copyright <2018-2025> <scott.e.graves@protonmail.com>
 | 
			
		||||
 | 
			
		||||
  Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
  of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
  in the Software without restriction, including without limitation the rights
 | 
			
		||||
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
  copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
  furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
  The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
  copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
  SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
#ifndef REPERTORY_INCLUDE_UI_MGMT_APP_CONFIG_HPP_
 | 
			
		||||
#define REPERTORY_INCLUDE_UI_MGMT_APP_CONFIG_HPP_
 | 
			
		||||
 | 
			
		||||
#include "types/repertory.hpp"
 | 
			
		||||
 | 
			
		||||
namespace repertory::ui {
 | 
			
		||||
class mgmt_app_config final {
 | 
			
		||||
public:
 | 
			
		||||
  mgmt_app_config();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  atomic<std::string> api_password_{"repertory"};
 | 
			
		||||
  std::atomic<std::uint16_t> api_port_{default_ui_mgmt_port};
 | 
			
		||||
  atomic<std::string> api_user_{"repertory"};
 | 
			
		||||
  std::unordered_map<provider_type,
 | 
			
		||||
                     std::unordered_map<std::string, std::string>>
 | 
			
		||||
      locations_;
 | 
			
		||||
  mutable std::recursive_mutex mtx_;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  void save() const;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  [[nodiscard]] auto to_json() const -> nlohmann::json;
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_api_password() const -> std::string {
 | 
			
		||||
    return api_password_;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_api_port() const -> std::uint16_t { return api_port_; }
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_api_user() const -> std::string { return api_user_; }
 | 
			
		||||
 | 
			
		||||
  [[nodiscard]] auto get_mount_location(provider_type prov,
 | 
			
		||||
                                        std::string_view name) const
 | 
			
		||||
      -> std::string;
 | 
			
		||||
 | 
			
		||||
  void set_api_password(std::string_view api_password);
 | 
			
		||||
 | 
			
		||||
  void set_api_port(std::uint16_t api_port);
 | 
			
		||||
 | 
			
		||||
  void set_api_user(std::string_view api_user);
 | 
			
		||||
 | 
			
		||||
  void set_mount_location(provider_type prov, std::string_view name,
 | 
			
		||||
                          std::string_view location);
 | 
			
		||||
};
 | 
			
		||||
} // namespace repertory::ui
 | 
			
		||||
 | 
			
		||||
#endif // REPERTORY_INCLUDE_UI_MGMT_APP_CONFIG_HPP_
 | 
			
		||||
@@ -44,7 +44,7 @@ auto main(int argc, char **argv) -> int {
 | 
			
		||||
  std::vector<const char *> args;
 | 
			
		||||
  {
 | 
			
		||||
    auto args_span = std::span(argv, static_cast<std::size_t>(argc));
 | 
			
		||||
    std::copy(args_span.begin(), args_span.end(), std::back_inserter(args));
 | 
			
		||||
    std::ranges::copy(args_span, std::back_inserter(args));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (argc == 1) {
 | 
			
		||||
@@ -105,7 +105,8 @@ auto main(int argc, char **argv) -> int {
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    } else if ((prov == provider_type::s3) || (prov == provider_type::sia)) {
 | 
			
		||||
    } else if ((prov == provider_type::s3) || (prov == provider_type::sia) ||
 | 
			
		||||
               (prov == provider_type::encrypt)) {
 | 
			
		||||
      std::string data;
 | 
			
		||||
      res = utils::cli::parse_string_option(
 | 
			
		||||
          args, utils::cli::options::name_option, data);
 | 
			
		||||
@@ -115,9 +116,9 @@ auto main(int argc, char **argv) -> int {
 | 
			
		||||
          if (prov == provider_type::sia) {
 | 
			
		||||
            unique_id = "default";
 | 
			
		||||
          } else {
 | 
			
		||||
            std::cerr << "Name of "
 | 
			
		||||
            std::cerr << "Configuration name for '"
 | 
			
		||||
                      << app_config::get_provider_display_name(prov)
 | 
			
		||||
                      << " instance not provided" << std::endl;
 | 
			
		||||
                      << "' was not provided" << std::endl;
 | 
			
		||||
            res = exit_code::invalid_syntax;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
@@ -146,10 +147,17 @@ auto main(int argc, char **argv) -> int {
 | 
			
		||||
           (res == exit_code::option_not_found) &&
 | 
			
		||||
           (idx < utils::cli::options::option_list.size());
 | 
			
		||||
           idx++) {
 | 
			
		||||
        try {
 | 
			
		||||
          res = cli::actions::perform_action(
 | 
			
		||||
              utils::cli::options::option_list[idx], args, data_directory, prov,
 | 
			
		||||
              unique_id, user, password);
 | 
			
		||||
        } catch (const std::exception &ex) {
 | 
			
		||||
          res = exit_code::exception;
 | 
			
		||||
        } catch (...) {
 | 
			
		||||
          res = exit_code::exception;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (res == exit_code::option_not_found) {
 | 
			
		||||
        res = cli::actions::mount(args, data_directory, mount_result, prov,
 | 
			
		||||
                                  remote_host, remote_port, unique_id);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										754
									
								
								repertory/repertory/src/ui/handlers.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										754
									
								
								repertory/repertory/src/ui/handlers.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,754 @@
 | 
			
		||||
/*
 | 
			
		||||
  Copyright <2018-2025> <scott.e.graves@protonmail.com>
 | 
			
		||||
 | 
			
		||||
  Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
  of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
  in the Software without restriction, including without limitation the rights
 | 
			
		||||
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
  copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
  furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
  The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
  copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
  SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
#include "ui/handlers.hpp"
 | 
			
		||||
 | 
			
		||||
#include "app_config.hpp"
 | 
			
		||||
#include "events/event_system.hpp"
 | 
			
		||||
#include "types/repertory.hpp"
 | 
			
		||||
#include "ui/mgmt_app_config.hpp"
 | 
			
		||||
#include "utils/collection.hpp"
 | 
			
		||||
#include "utils/common.hpp"
 | 
			
		||||
#include "utils/config.hpp"
 | 
			
		||||
#include "utils/error_utils.hpp"
 | 
			
		||||
#include "utils/file.hpp"
 | 
			
		||||
#include "utils/hash.hpp"
 | 
			
		||||
#include "utils/path.hpp"
 | 
			
		||||
#include "utils/string.hpp"
 | 
			
		||||
 | 
			
		||||
#include <boost/process/v1/args.hpp>
 | 
			
		||||
#include <boost/process/v1/child.hpp>
 | 
			
		||||
#include <boost/process/v1/io.hpp>
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
[[nodiscard]] auto decrypt(std::string_view data, std::string_view password)
 | 
			
		||||
    -> std::string {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
  if (data.empty()) {
 | 
			
		||||
    return std::string{data};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  repertory::data_buffer decoded;
 | 
			
		||||
  if (not repertory::utils::collection::from_hex_string(data, decoded)) {
 | 
			
		||||
    throw repertory::utils::error::create_exception(function_name,
 | 
			
		||||
                                                    {"decryption failed"});
 | 
			
		||||
  }
 | 
			
		||||
  repertory::data_buffer buffer(decoded.size());
 | 
			
		||||
 | 
			
		||||
  auto key = repertory::utils::encryption::create_hash_blake2b_256(password);
 | 
			
		||||
 | 
			
		||||
  unsigned long long size{};
 | 
			
		||||
  auto res = crypto_aead_xchacha20poly1305_ietf_decrypt(
 | 
			
		||||
      reinterpret_cast<unsigned char *>(buffer.data()), &size, nullptr,
 | 
			
		||||
      reinterpret_cast<const unsigned char *>(
 | 
			
		||||
          &decoded.at(crypto_aead_xchacha20poly1305_IETF_NPUBBYTES)),
 | 
			
		||||
      decoded.size() - crypto_aead_xchacha20poly1305_IETF_NPUBBYTES,
 | 
			
		||||
      reinterpret_cast<const unsigned char *>(REPERTORY.data()),
 | 
			
		||||
      REPERTORY.length(),
 | 
			
		||||
      reinterpret_cast<const unsigned char *>(decoded.data()),
 | 
			
		||||
      reinterpret_cast<const unsigned char *>(key.data()));
 | 
			
		||||
  if (res != 0) {
 | 
			
		||||
    throw repertory::utils::error::create_exception(function_name,
 | 
			
		||||
                                                    {"decryption failed"});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
      buffer.begin(),
 | 
			
		||||
      std::next(buffer.begin(), static_cast<std::int64_t>(size)),
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] auto decrypt_value(const repertory::ui::mgmt_app_config *config,
 | 
			
		||||
                                 std::string_view key, std::string_view value,
 | 
			
		||||
                                 bool &skip) -> std::string {
 | 
			
		||||
  auto last_key{key};
 | 
			
		||||
  auto parts = repertory::utils::string::split(key, '.', false);
 | 
			
		||||
  if (parts.size() > 1U) {
 | 
			
		||||
    last_key = parts.at(parts.size() - 1U);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (last_key == repertory::JSON_API_PASSWORD ||
 | 
			
		||||
      last_key == repertory::JSON_ENCRYPTION_TOKEN ||
 | 
			
		||||
      last_key == repertory::JSON_SECRET_KEY) {
 | 
			
		||||
    auto decrypted = decrypt(value, config->get_api_password());
 | 
			
		||||
    if (decrypted.empty()) {
 | 
			
		||||
      skip = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return decrypted;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return std::string{value};
 | 
			
		||||
}
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
namespace repertory::ui {
 | 
			
		||||
handlers::handlers(mgmt_app_config *config, httplib::Server *server)
 | 
			
		||||
    : config_(config),
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
      repertory_binary_(utils::path::combine(".", {"repertory.exe"})),
 | 
			
		||||
#else  // !defined(_WIN32)
 | 
			
		||||
      repertory_binary_(utils::path::combine(".", {"repertory"})),
 | 
			
		||||
#endif // defined(_WIN32)
 | 
			
		||||
      server_(server) {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  server_->set_socket_options([](auto &&sock) {
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
    int enable{1};
 | 
			
		||||
    setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
 | 
			
		||||
               reinterpret_cast<const char *>(&enable), sizeof(enable));
 | 
			
		||||
#else  //  !defined(_WIN32)
 | 
			
		||||
    linger opt{1, 0};
 | 
			
		||||
    setsockopt(sock, SOL_SOCKET, SO_LINGER,
 | 
			
		||||
               reinterpret_cast<const char *>(&opt), sizeof(opt));
 | 
			
		||||
#endif // defined(_WIN32)
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  server_->set_pre_routing_handler(
 | 
			
		||||
      [this](const httplib::Request &req,
 | 
			
		||||
             auto &&res) -> httplib::Server::HandlerResponse {
 | 
			
		||||
        if (req.path == "/api/v1/nonce" || req.path == "/ui" ||
 | 
			
		||||
            req.path.starts_with("/ui/")) {
 | 
			
		||||
          return httplib::Server::HandlerResponse::Unhandled;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto auth =
 | 
			
		||||
            decrypt(req.get_param_value("auth"), config_->get_api_password());
 | 
			
		||||
        if (utils::string::begins_with(
 | 
			
		||||
                auth, fmt::format("{}_", config_->get_api_user()))) {
 | 
			
		||||
          auto nonce = auth.substr(config_->get_api_user().length() + 1U);
 | 
			
		||||
 | 
			
		||||
          mutex_lock lock(nonce_mtx_);
 | 
			
		||||
          if (nonce_lookup_.contains(nonce)) {
 | 
			
		||||
            nonce_lookup_.erase(nonce);
 | 
			
		||||
            return httplib::Server::HandlerResponse::Unhandled;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        res.status = http_error_codes::unauthorized;
 | 
			
		||||
        return httplib::Server::HandlerResponse::Handled;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
  server_->set_exception_handler([](const httplib::Request &req,
 | 
			
		||||
                                    httplib::Response &res,
 | 
			
		||||
                                    std::exception_ptr ptr) {
 | 
			
		||||
    json data{
 | 
			
		||||
        {"path", req.path},
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      std::rethrow_exception(ptr);
 | 
			
		||||
    } catch (const std::exception &e) {
 | 
			
		||||
      data["error"] = (e.what() == nullptr) ? "unknown error" : e.what();
 | 
			
		||||
      utils::error::raise_error(function_name, e,
 | 
			
		||||
                                "failed request: " + req.path);
 | 
			
		||||
    } catch (...) {
 | 
			
		||||
      data["error"] = "unknown error";
 | 
			
		||||
      utils::error::raise_error(function_name, "unknown error",
 | 
			
		||||
                                "failed request: " + req.path);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    res.set_content(data.dump(), "application/json");
 | 
			
		||||
    res.status = utils::string::ends_with(data["error"].get<std::string>(),
 | 
			
		||||
                                          "|decryption failed")
 | 
			
		||||
                     ? http_error_codes::unauthorized
 | 
			
		||||
                     : http_error_codes::internal_error;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  server->Get("/api/v1/locations", [](auto && /* req */, auto &&res) {
 | 
			
		||||
    handle_get_available_locations(res);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  server->Get("/api/v1/mount",
 | 
			
		||||
              [this](auto &&req, auto &&res) { handle_get_mount(req, res); });
 | 
			
		||||
 | 
			
		||||
  server->Get("/api/v1/mount_location", [this](auto &&req, auto &&res) {
 | 
			
		||||
    handle_get_mount_location(req, res);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  server->Get("/api/v1/mount_list", [](auto && /* req */, auto &&res) {
 | 
			
		||||
    handle_get_mount_list(res);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  server->Get("/api/v1/mount_status", [this](auto &&req, auto &&res) {
 | 
			
		||||
    handle_get_mount_status(req, res);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  server->Get("/api/v1/nonce",
 | 
			
		||||
              [this](auto && /* req */, auto &&res) { handle_get_nonce(res); });
 | 
			
		||||
 | 
			
		||||
  server->Get("/api/v1/settings", [this](auto && /* req */, auto &&res) {
 | 
			
		||||
    handle_get_settings(res);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  server->Post("/api/v1/add_mount", [this](auto &&req, auto &&res) {
 | 
			
		||||
    handle_post_add_mount(req, res);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  server->Post("/api/v1/mount",
 | 
			
		||||
               [this](auto &&req, auto &&res) { handle_post_mount(req, res); });
 | 
			
		||||
 | 
			
		||||
  server->Put("/api/v1/mount_location", [this](auto &&req, auto &&res) {
 | 
			
		||||
    handle_put_mount_location(req, res);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  server->Put("/api/v1/set_value_by_name", [this](auto &&req, auto &&res) {
 | 
			
		||||
    handle_put_set_value_by_name(req, res);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  server->Put("/api/v1/settings", [this](auto &&req, auto &&res) {
 | 
			
		||||
    handle_put_settings(req, res);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  static std::atomic<httplib::Server *> this_server{server_};
 | 
			
		||||
  static const auto quit_handler = [](int /* sig */) {
 | 
			
		||||
    auto *ptr = this_server.load();
 | 
			
		||||
    if (ptr == nullptr) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this_server = nullptr;
 | 
			
		||||
    ptr->stop();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  std::signal(SIGINT, quit_handler);
 | 
			
		||||
#if !defined(_WIN32)
 | 
			
		||||
  std::signal(SIGQUIT, quit_handler);
 | 
			
		||||
#endif // !defined(_WIN32)
 | 
			
		||||
  std::signal(SIGTERM, quit_handler);
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
  system(fmt::format(
 | 
			
		||||
             R"(start "Repertory Management Portal" "http://127.0.0.1:{}/ui")",
 | 
			
		||||
             config_->get_api_port())
 | 
			
		||||
             .c_str());
 | 
			
		||||
#elif defined(__linux__)
 | 
			
		||||
  system(fmt::format(R"(xdg-open "http://127.0.0.1:{}/ui")",
 | 
			
		||||
                     config_->get_api_port())
 | 
			
		||||
             .c_str());
 | 
			
		||||
#else // error
 | 
			
		||||
  build fails here
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  std::uint16_t port{};
 | 
			
		||||
  if (not utils::get_next_available_port(config_->get_api_port(), port)) {
 | 
			
		||||
    fmt::println("failed to detect if port is available|{}",
 | 
			
		||||
                 config_->get_api_port());
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (port != config_->get_api_port()) {
 | 
			
		||||
    fmt::println("failed to listen on port|{}|next available|{}",
 | 
			
		||||
                 config_->get_api_port(), port);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  event_system::instance().start();
 | 
			
		||||
 | 
			
		||||
  nonce_thread_ =
 | 
			
		||||
      std::make_unique<std::thread>([this]() { removed_expired_nonces(); });
 | 
			
		||||
 | 
			
		||||
  server_->listen("127.0.0.1", config_->get_api_port());
 | 
			
		||||
  if (this_server != nullptr) {
 | 
			
		||||
    this_server = nullptr;
 | 
			
		||||
    server_->stop();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
handlers::~handlers() {
 | 
			
		||||
  if (nonce_thread_) {
 | 
			
		||||
    stop_requested = true;
 | 
			
		||||
 | 
			
		||||
    unique_mutex_lock lock(nonce_mtx_);
 | 
			
		||||
    nonce_notify_.notify_all();
 | 
			
		||||
    lock.unlock();
 | 
			
		||||
 | 
			
		||||
    nonce_thread_->join();
 | 
			
		||||
    nonce_thread_.reset();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  event_system::instance().stop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto handlers::data_directory_exists(provider_type prov,
 | 
			
		||||
                                     std::string_view name) const -> bool {
 | 
			
		||||
  auto data_dir = utils::path::combine(app_config::get_root_data_directory(),
 | 
			
		||||
                                       {
 | 
			
		||||
                                           app_config::get_provider_name(prov),
 | 
			
		||||
                                           name,
 | 
			
		||||
                                       });
 | 
			
		||||
  auto ret = utils::file::directory{data_dir}.exists();
 | 
			
		||||
  if (ret) {
 | 
			
		||||
    return ret;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  unique_mutex_lock lock(mtx_);
 | 
			
		||||
  mtx_lookup_.erase(
 | 
			
		||||
      fmt::format("{}-{}", name, app_config::get_provider_name(prov)));
 | 
			
		||||
  lock.unlock();
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handlers::handle_put_mount_location(const httplib::Request &req,
 | 
			
		||||
                                         httplib::Response &res) const {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  auto prov = provider_type_from_string(req.get_param_value("type"));
 | 
			
		||||
  auto name = req.get_param_value("name");
 | 
			
		||||
  auto location = req.get_param_value("location");
 | 
			
		||||
 | 
			
		||||
  if (not data_directory_exists(prov, name)) {
 | 
			
		||||
    res.status = http_error_codes::not_found;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  config_->set_mount_location(prov, name, location);
 | 
			
		||||
  res.status = http_error_codes::ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handlers::handle_get_available_locations(httplib::Response &res) {
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
  constexpr const std::array<std::string_view, 26U> letters{
 | 
			
		||||
      "a:", "b:", "c:", "d:", "e:", "f:", "g:", "h:", "i:",
 | 
			
		||||
      "j:", "k:", "l:", "m:", "n:", "o:", "p:", "q:", "r:",
 | 
			
		||||
      "s:", "t:", "u:", "v:", "w:", "x:", "y:", "z:",
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  auto available = std::accumulate(
 | 
			
		||||
      letters.begin(), letters.end(), std::vector<std::string_view>(),
 | 
			
		||||
      [](auto &&vec, auto &&letter) -> std::vector<std::string_view> {
 | 
			
		||||
        if (utils::file::directory{utils::path::combine(letter, {"\\"})}
 | 
			
		||||
                .exists()) {
 | 
			
		||||
          return vec;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        vec.emplace_back(letter);
 | 
			
		||||
        return vec;
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
  res.set_content(nlohmann::json(available).dump(), "application/json");
 | 
			
		||||
#else  // !defined(_WIN32)
 | 
			
		||||
  res.set_content(nlohmann::json(std::vector<std::string_view>()).dump(),
 | 
			
		||||
                  "application/json");
 | 
			
		||||
#endif // defined(_WIN32)
 | 
			
		||||
 | 
			
		||||
  res.status = http_error_codes::ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handlers::handle_get_mount(const httplib::Request &req,
 | 
			
		||||
                                httplib::Response &res) const {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  auto prov = provider_type_from_string(req.get_param_value("type"));
 | 
			
		||||
  auto name = req.get_param_value("name");
 | 
			
		||||
 | 
			
		||||
  if (not data_directory_exists(prov, name)) {
 | 
			
		||||
    res.status = http_error_codes::not_found;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto lines = launch_process(prov, name, {"-dc"});
 | 
			
		||||
 | 
			
		||||
  if (lines.at(0U) != "0") {
 | 
			
		||||
    throw utils::error::create_exception(function_name, {
 | 
			
		||||
                                                            "command failed",
 | 
			
		||||
                                                            lines.at(0U),
 | 
			
		||||
                                                        });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  lines.erase(lines.begin());
 | 
			
		||||
 | 
			
		||||
  auto result = nlohmann::json::parse(utils::string::join(lines, '\n'));
 | 
			
		||||
  clean_json_config(prov, result);
 | 
			
		||||
 | 
			
		||||
  res.set_content(result.dump(), "application/json");
 | 
			
		||||
  res.status = http_error_codes::ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handlers::handle_get_mount_list(httplib::Response &res) {
 | 
			
		||||
  auto data_dir = utils::file::directory{app_config::get_root_data_directory()};
 | 
			
		||||
 | 
			
		||||
  nlohmann::json result;
 | 
			
		||||
  const auto process_dir = [&data_dir, &result](std::string_view name) {
 | 
			
		||||
    auto name_dir = data_dir.get_directory(name);
 | 
			
		||||
    if (not name_dir) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const auto &dir : name_dir->get_directories()) {
 | 
			
		||||
      if (not dir->get_file("config.json")) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      result[name].emplace_back(
 | 
			
		||||
          utils::path::strip_to_file_name(dir->get_path()));
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  process_dir("encrypt");
 | 
			
		||||
  process_dir("remote");
 | 
			
		||||
  process_dir("s3");
 | 
			
		||||
  process_dir("sia");
 | 
			
		||||
 | 
			
		||||
  res.set_content(result.dump(), "application/json");
 | 
			
		||||
  res.status = http_error_codes::ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handlers::handle_get_mount_location(const httplib::Request &req,
 | 
			
		||||
                                         httplib::Response &res) const {
 | 
			
		||||
  auto name = req.get_param_value("name");
 | 
			
		||||
  auto prov = provider_type_from_string(req.get_param_value("type"));
 | 
			
		||||
 | 
			
		||||
  if (not data_directory_exists(prov, name)) {
 | 
			
		||||
    res.status = http_error_codes::not_found;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  res.set_content(
 | 
			
		||||
      nlohmann::json({
 | 
			
		||||
                         {"Location", config_->get_mount_location(prov, name)},
 | 
			
		||||
                     })
 | 
			
		||||
          .dump(),
 | 
			
		||||
      "application/json");
 | 
			
		||||
  res.status = http_error_codes::ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handlers::handle_get_mount_status(const httplib::Request &req,
 | 
			
		||||
                                       httplib::Response &res) const {
 | 
			
		||||
  auto name = req.get_param_value("name");
 | 
			
		||||
  auto prov = provider_type_from_string(req.get_param_value("type"));
 | 
			
		||||
 | 
			
		||||
  if (not data_directory_exists(prov, name)) {
 | 
			
		||||
    res.status = http_error_codes::not_found;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto lines = launch_process(prov, name, {"-status"});
 | 
			
		||||
 | 
			
		||||
  auto result = nlohmann::json::parse(utils::string::join(lines, '\n'));
 | 
			
		||||
  if (result.at("Location").get<std::string>().empty()) {
 | 
			
		||||
    result.at("Location") = config_->get_mount_location(prov, name);
 | 
			
		||||
  } else if (result.at("Active").get<bool>()) {
 | 
			
		||||
    config_->set_mount_location(prov, name,
 | 
			
		||||
                                result.at("Location").get<std::string>());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  res.set_content(result.dump(), "application/json");
 | 
			
		||||
  res.status = http_error_codes::ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handlers::handle_get_nonce(httplib::Response &res) {
 | 
			
		||||
  mutex_lock lock(nonce_mtx_);
 | 
			
		||||
 | 
			
		||||
  nonce_data nonce{};
 | 
			
		||||
  nonce_lookup_[nonce.nonce] = nonce;
 | 
			
		||||
 | 
			
		||||
  nlohmann::json data({{"nonce", nonce.nonce}});
 | 
			
		||||
  res.set_content(data.dump(), "application/json");
 | 
			
		||||
  res.status = http_error_codes::ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handlers::handle_get_settings(httplib::Response &res) const {
 | 
			
		||||
  auto settings = config_->to_json();
 | 
			
		||||
  settings[JSON_API_PASSWORD] = "";
 | 
			
		||||
  settings.erase(JSON_MOUNT_LOCATIONS);
 | 
			
		||||
  res.set_content(settings.dump(), "application/json");
 | 
			
		||||
  res.status = http_error_codes::ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handlers::handle_post_add_mount(const httplib::Request &req,
 | 
			
		||||
                                     httplib::Response &res) const {
 | 
			
		||||
  auto name = req.get_param_value("name");
 | 
			
		||||
  auto prov = provider_type_from_string(req.get_param_value("type"));
 | 
			
		||||
  if (data_directory_exists(prov, name)) {
 | 
			
		||||
    res.status = http_error_codes::ok;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto cfg = nlohmann::json::parse(req.get_param_value("config"));
 | 
			
		||||
 | 
			
		||||
  std::map<std::string, std::string> values{};
 | 
			
		||||
  for (const auto &[key, value] : cfg.items()) {
 | 
			
		||||
    if (value.is_object()) {
 | 
			
		||||
      for (const auto &[key2, value2] : value.items()) {
 | 
			
		||||
        auto sub_key = fmt::format("{}.{}", key, key2);
 | 
			
		||||
        auto skip{false};
 | 
			
		||||
        auto decrypted = decrypt_value(
 | 
			
		||||
            config_, sub_key, value2.template get<std::string>(), skip);
 | 
			
		||||
        if (skip) {
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
        values[sub_key] = decrypted;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto skip{false};
 | 
			
		||||
    auto decrypted =
 | 
			
		||||
        decrypt_value(config_, key, value.template get<std::string>(), skip);
 | 
			
		||||
    if (skip) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
    values[key] = decrypted;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  launch_process(prov, name, {"-gc"});
 | 
			
		||||
  for (auto &[key, value] : values) {
 | 
			
		||||
    set_key_value(prov, name, key, value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  res.status = http_error_codes::ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handlers::handle_post_mount(const httplib::Request &req,
 | 
			
		||||
                                 httplib::Response &res) {
 | 
			
		||||
  auto name = req.get_param_value("name");
 | 
			
		||||
  auto prov = provider_type_from_string(req.get_param_value("type"));
 | 
			
		||||
 | 
			
		||||
  if (not data_directory_exists(prov, name)) {
 | 
			
		||||
    res.status = http_error_codes::not_found;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto location = utils::path::absolute(req.get_param_value("location"));
 | 
			
		||||
  auto unmount = utils::string::to_bool(req.get_param_value("unmount"));
 | 
			
		||||
 | 
			
		||||
  if (unmount) {
 | 
			
		||||
    launch_process(prov, name, {"-unmount"});
 | 
			
		||||
  } else {
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
    if (utils::file::directory{location}.exists()) {
 | 
			
		||||
#else  // !defined(_WIN32)
 | 
			
		||||
    if (not utils::file::directory{location}.exists()) {
 | 
			
		||||
#endif // defined(_WIN32)
 | 
			
		||||
      config_->set_mount_location(prov, name, "");
 | 
			
		||||
      res.status = http_error_codes::internal_error;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    config_->set_mount_location(prov, name, location);
 | 
			
		||||
 | 
			
		||||
    static std::mutex mount_mtx;
 | 
			
		||||
    mutex_lock lock(mount_mtx);
 | 
			
		||||
 | 
			
		||||
    launch_process(prov, name, {location}, true);
 | 
			
		||||
    launch_process(prov, name, {"-status"});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  res.status = http_error_codes::ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handlers::handle_put_set_value_by_name(const httplib::Request &req,
 | 
			
		||||
                                            httplib::Response &res) const {
 | 
			
		||||
  auto name = req.get_param_value("name");
 | 
			
		||||
  auto prov = provider_type_from_string(req.get_param_value("type"));
 | 
			
		||||
 | 
			
		||||
  if (not data_directory_exists(prov, name)) {
 | 
			
		||||
    res.status = http_error_codes::not_found;
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto key = req.get_param_value("key");
 | 
			
		||||
  auto value = req.get_param_value("value");
 | 
			
		||||
 | 
			
		||||
  auto skip{false};
 | 
			
		||||
  value = decrypt_value(config_, key, value, skip);
 | 
			
		||||
  if (not skip) {
 | 
			
		||||
    set_key_value(prov, name, key, value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  res.status = http_error_codes::ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handlers::handle_put_settings(const httplib::Request &req,
 | 
			
		||||
                                   httplib::Response &res) const {
 | 
			
		||||
  auto data = nlohmann::json::parse(req.get_param_value("data"));
 | 
			
		||||
 | 
			
		||||
  if (data.contains(JSON_API_PASSWORD)) {
 | 
			
		||||
    auto password = decrypt(data.at(JSON_API_PASSWORD).get<std::string>(),
 | 
			
		||||
                            config_->get_api_password());
 | 
			
		||||
    if (not password.empty()) {
 | 
			
		||||
      config_->set_api_password(password);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (data.contains(JSON_API_PORT)) {
 | 
			
		||||
    config_->set_api_port(
 | 
			
		||||
        utils::string::to_uint16(data.at(JSON_API_PORT).get<std::string>()));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (data.contains(JSON_API_USER)) {
 | 
			
		||||
    config_->set_api_user(data.at(JSON_API_USER).get<std::string>());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  res.status = http_error_codes::ok;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto handlers::launch_process(provider_type prov, std::string_view name,
 | 
			
		||||
                              std::vector<std::string> args,
 | 
			
		||||
                              bool background) const
 | 
			
		||||
    -> std::vector<std::string> {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  switch (prov) {
 | 
			
		||||
  case provider_type::encrypt:
 | 
			
		||||
    args.insert(args.begin(), "-en");
 | 
			
		||||
    args.insert(std::next(args.begin()), "-na");
 | 
			
		||||
    args.insert(std::next(args.begin(), 2U), std::string{name});
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case provider_type::remote: {
 | 
			
		||||
    auto parts = utils::string::split(name, '_', false);
 | 
			
		||||
    args.insert(args.begin(), "-rm");
 | 
			
		||||
    args.insert(std::next(args.begin()),
 | 
			
		||||
                fmt::format("{}:{}", parts.at(0U), parts.at(1U)));
 | 
			
		||||
  } break;
 | 
			
		||||
 | 
			
		||||
  case provider_type::s3:
 | 
			
		||||
    args.insert(args.begin(), "-s3");
 | 
			
		||||
    args.insert(std::next(args.begin()), "-na");
 | 
			
		||||
    args.insert(std::next(args.begin(), 2U), std::string{name});
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  case provider_type::sia:
 | 
			
		||||
    args.insert(args.begin(), "-na");
 | 
			
		||||
    args.insert(std::next(args.begin()), std::string{name});
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  default:
 | 
			
		||||
    throw utils::error::create_exception(function_name,
 | 
			
		||||
                                         {
 | 
			
		||||
                                             "provider is not supported",
 | 
			
		||||
                                             provider_type_to_string(prov),
 | 
			
		||||
                                             name,
 | 
			
		||||
                                         });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  unique_mutex_lock lock(mtx_);
 | 
			
		||||
  auto &inst_mtx = mtx_lookup_[fmt::format(
 | 
			
		||||
      "{}-{}", name, app_config::get_provider_name(prov))];
 | 
			
		||||
  lock.unlock();
 | 
			
		||||
 | 
			
		||||
  recur_mutex_lock inst_lock(inst_mtx);
 | 
			
		||||
  if (background) {
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
    std::array<char, MAX_PATH + 1U> path{};
 | 
			
		||||
    ::GetSystemDirectoryA(path.data(), path.size());
 | 
			
		||||
 | 
			
		||||
    args.insert(args.begin(), utils::path::combine(path.data(), {"cmd.exe"}));
 | 
			
		||||
    args.insert(std::next(args.begin()), "/c");
 | 
			
		||||
    args.insert(std::next(args.begin(), 2U), "start");
 | 
			
		||||
    args.insert(std::next(args.begin(), 3U), "");
 | 
			
		||||
    args.insert(std::next(args.begin(), 4U), "/MIN");
 | 
			
		||||
    args.insert(std::next(args.begin(), 5U), repertory_binary_);
 | 
			
		||||
#else  // !defined(_WIN32)
 | 
			
		||||
    args.insert(args.begin(), repertory_binary_);
 | 
			
		||||
#endif // defined(_WIN32)
 | 
			
		||||
 | 
			
		||||
    std::vector<const char *> exec_args;
 | 
			
		||||
    exec_args.reserve(args.size() + 1U);
 | 
			
		||||
    for (const auto &arg : args) {
 | 
			
		||||
      exec_args.push_back(arg.c_str());
 | 
			
		||||
    }
 | 
			
		||||
    exec_args.push_back(nullptr);
 | 
			
		||||
 | 
			
		||||
#if defined(_WIN32)
 | 
			
		||||
    _spawnv(_P_DETACH, exec_args.at(0U),
 | 
			
		||||
            const_cast<char *const *>(exec_args.data()));
 | 
			
		||||
#else  // !defined(_WIN32)
 | 
			
		||||
    auto pid = fork();
 | 
			
		||||
    if (pid == 0) {
 | 
			
		||||
      setsid();
 | 
			
		||||
      chdir("/");
 | 
			
		||||
      close(STDIN_FILENO);
 | 
			
		||||
      close(STDOUT_FILENO);
 | 
			
		||||
      close(STDERR_FILENO);
 | 
			
		||||
      open("/dev/null", O_RDONLY);
 | 
			
		||||
      open("/dev/null", O_WRONLY);
 | 
			
		||||
      open("/dev/null", O_WRONLY);
 | 
			
		||||
 | 
			
		||||
      execvp(exec_args.at(0U), const_cast<char *const *>(exec_args.data()));
 | 
			
		||||
    } else {
 | 
			
		||||
      signal(SIGCHLD, SIG_IGN);
 | 
			
		||||
    }
 | 
			
		||||
#endif // defined(_WIN32)
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  boost::process::v1::ipstream out;
 | 
			
		||||
  boost::process::v1::child proc(repertory_binary_,
 | 
			
		||||
                                 boost::process::v1::args(args),
 | 
			
		||||
                                 boost::process::v1::std_out > out);
 | 
			
		||||
 | 
			
		||||
  std::string data;
 | 
			
		||||
  std::string line;
 | 
			
		||||
  while (out && std::getline(out, line)) {
 | 
			
		||||
    data += line + "\n";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return utils::string::split(utils::string::replace(data, "\r", ""), '\n',
 | 
			
		||||
                              false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handlers::removed_expired_nonces() {
 | 
			
		||||
  unique_mutex_lock lock(nonce_mtx_);
 | 
			
		||||
  lock.unlock();
 | 
			
		||||
 | 
			
		||||
  while (not stop_requested) {
 | 
			
		||||
    lock.lock();
 | 
			
		||||
    auto nonces = nonce_lookup_;
 | 
			
		||||
    lock.unlock();
 | 
			
		||||
 | 
			
		||||
    for (const auto &[key, value] : nonces) {
 | 
			
		||||
      if (std::chrono::duration_cast<std::chrono::seconds>(
 | 
			
		||||
              std::chrono::system_clock::now() - value.creation)
 | 
			
		||||
              .count() >= nonce_timeout) {
 | 
			
		||||
        lock.lock();
 | 
			
		||||
        nonce_lookup_.erase(key);
 | 
			
		||||
        lock.unlock();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (stop_requested) {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lock.lock();
 | 
			
		||||
    if (stop_requested) {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    nonce_notify_.wait_for(lock, std::chrono::seconds(1U));
 | 
			
		||||
    lock.unlock();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void handlers::set_key_value(provider_type prov, std::string_view name,
 | 
			
		||||
                             std::string_view key,
 | 
			
		||||
                             std::string_view value) const {
 | 
			
		||||
  std::vector<std::string> args;
 | 
			
		||||
  args.emplace_back("-set");
 | 
			
		||||
  args.emplace_back(key);
 | 
			
		||||
  args.emplace_back(value);
 | 
			
		||||
  launch_process(prov, name, args, false);
 | 
			
		||||
}
 | 
			
		||||
} // namespace repertory::ui
 | 
			
		||||
							
								
								
									
										202
									
								
								repertory/repertory/src/ui/mgmt_app_config.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								repertory/repertory/src/ui/mgmt_app_config.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,202 @@
 | 
			
		||||
/*
 | 
			
		||||
  Copyright <2018-2025> <scott.e.graves@protonmail.com>
 | 
			
		||||
 | 
			
		||||
  Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
  of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
  in the Software without restriction, including without limitation the rights
 | 
			
		||||
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
  copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
  furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
  The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
  copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
  SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
#include "ui/mgmt_app_config.hpp"
 | 
			
		||||
 | 
			
		||||
#include "app_config.hpp"
 | 
			
		||||
#include "utils/error_utils.hpp"
 | 
			
		||||
#include "utils/file.hpp"
 | 
			
		||||
#include "utils/path.hpp"
 | 
			
		||||
#include "utils/unix.hpp"
 | 
			
		||||
#include "utils/windows.hpp"
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
[[nodiscard]] auto from_json(const nlohmann::json &json)
 | 
			
		||||
    -> std::unordered_map<repertory::provider_type,
 | 
			
		||||
                          std::unordered_map<std::string, std::string>> {
 | 
			
		||||
  std::unordered_map<repertory::provider_type,
 | 
			
		||||
                     std::unordered_map<std::string, std::string>>
 | 
			
		||||
      map_of_maps{
 | 
			
		||||
          {repertory::provider_type::encrypt, nlohmann::json::object()},
 | 
			
		||||
          {repertory::provider_type::remote, nlohmann::json::object()},
 | 
			
		||||
          {repertory::provider_type::s3, nlohmann::json::object()},
 | 
			
		||||
          {repertory::provider_type::sia, nlohmann::json::object()},
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
  if (json.is_null() || json.empty()) {
 | 
			
		||||
    return map_of_maps;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for (auto &[prov, map] : map_of_maps) {
 | 
			
		||||
    auto prov_str = repertory::provider_type_to_string(prov);
 | 
			
		||||
    if (!json.contains(prov_str)) {
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const auto &[key, value] : json.at(prov_str).items()) {
 | 
			
		||||
      map[key] = value;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return map_of_maps;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
[[nodiscard]] auto map_to_json(const auto &map_of_maps) -> nlohmann::json {
 | 
			
		||||
  auto json = nlohmann::json::object();
 | 
			
		||||
  for (const auto &[prov, map] : map_of_maps) {
 | 
			
		||||
    for (const auto &[key, value] : map) {
 | 
			
		||||
      json[repertory::provider_type_to_string(prov)][key] = value;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return json;
 | 
			
		||||
}
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
namespace repertory::ui {
 | 
			
		||||
mgmt_app_config::mgmt_app_config() {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  auto config_file =
 | 
			
		||||
      utils::path::combine(app_config::get_root_data_directory(), {"ui.json"});
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    if (not utils::file::directory{app_config::get_root_data_directory()}
 | 
			
		||||
                .create_directory()) {
 | 
			
		||||
      throw utils::error::create_exception(
 | 
			
		||||
          function_name, {
 | 
			
		||||
                             "failed to create directory",
 | 
			
		||||
                             app_config::get_root_data_directory(),
 | 
			
		||||
                         });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    nlohmann::json data;
 | 
			
		||||
    if (utils::file::read_json_file(config_file, data)) {
 | 
			
		||||
      api_password_ = data.at(JSON_API_PASSWORD).get<std::string>();
 | 
			
		||||
      api_port_ = data.at(JSON_API_PORT).get<std::uint16_t>();
 | 
			
		||||
      api_user_ = data.at(JSON_API_USER).get<std::string>();
 | 
			
		||||
      locations_ = from_json(data.at(JSON_MOUNT_LOCATIONS));
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    utils::error::raise_error(
 | 
			
		||||
        function_name, utils::get_last_error_code(),
 | 
			
		||||
        fmt::format("failed to read file|{}", config_file));
 | 
			
		||||
    save();
 | 
			
		||||
  } catch (const std::exception &ex) {
 | 
			
		||||
    utils::error::raise_error(
 | 
			
		||||
        function_name, ex, fmt::format("failed to read file|{}", config_file));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto mgmt_app_config::get_mount_location(provider_type prov,
 | 
			
		||||
                                         std::string_view name) const
 | 
			
		||||
    -> std::string {
 | 
			
		||||
  recur_mutex_lock lock(mtx_);
 | 
			
		||||
  if (locations_.contains(prov) &&
 | 
			
		||||
      locations_.at(prov).contains(std::string{name})) {
 | 
			
		||||
    return locations_.at(prov).at(std::string{name});
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgmt_app_config::save() const {
 | 
			
		||||
  REPERTORY_USES_FUNCTION_NAME();
 | 
			
		||||
 | 
			
		||||
  auto config_file =
 | 
			
		||||
      utils::path::combine(app_config::get_root_data_directory(), {"ui.json"});
 | 
			
		||||
 | 
			
		||||
  try {
 | 
			
		||||
    if (not utils::file::directory{app_config::get_root_data_directory()}
 | 
			
		||||
                .create_directory()) {
 | 
			
		||||
      utils::error::raise_error(
 | 
			
		||||
          function_name, fmt::format("failed to create directory|{}",
 | 
			
		||||
                                     app_config::get_root_data_directory()));
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (utils::file::write_json_file(config_file, to_json())) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    utils::error::raise_error(
 | 
			
		||||
        function_name, utils::get_last_error_code(),
 | 
			
		||||
        fmt::format("failed to save file|{}", config_file));
 | 
			
		||||
  } catch (const std::exception &ex) {
 | 
			
		||||
    utils::error::raise_error(
 | 
			
		||||
        function_name, ex, fmt::format("failed to save file|{}", config_file));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgmt_app_config::set_api_password(std::string_view api_password) {
 | 
			
		||||
  if (api_password_ == std::string{api_password}) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  api_password_ = std::string{api_password};
 | 
			
		||||
  save();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgmt_app_config::set_api_port(std::uint16_t api_port) {
 | 
			
		||||
  if (api_port_ == api_port) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  api_port_ = api_port;
 | 
			
		||||
  save();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgmt_app_config::set_api_user(std::string_view api_user) {
 | 
			
		||||
  if (api_user_ == std::string{api_user}) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  api_user_ = std::string{api_user};
 | 
			
		||||
  save();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mgmt_app_config::set_mount_location(provider_type prov,
 | 
			
		||||
                                         std::string_view name,
 | 
			
		||||
                                         std::string_view location) {
 | 
			
		||||
  if (name.empty()) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  recur_mutex_lock lock(mtx_);
 | 
			
		||||
  if (locations_[prov][std::string{name}] == std::string{location}) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  locations_[prov][std::string{name}] = std::string{location};
 | 
			
		||||
 | 
			
		||||
  save();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto mgmt_app_config::to_json() const -> nlohmann::json {
 | 
			
		||||
  nlohmann::json data;
 | 
			
		||||
  data[JSON_API_PASSWORD] = api_password_;
 | 
			
		||||
  data[JSON_API_PORT] = api_port_;
 | 
			
		||||
  data[JSON_API_USER] = api_user_;
 | 
			
		||||
  data[JSON_MOUNT_LOCATIONS] = map_to_json(locations_);
 | 
			
		||||
  return data;
 | 
			
		||||
}
 | 
			
		||||
} // namespace repertory::ui
 | 
			
		||||
@@ -54,21 +54,31 @@ namespace repertory {
 | 
			
		||||
struct local_s3 final {
 | 
			
		||||
  static constexpr const provider_type type{provider_type::s3};
 | 
			
		||||
  static constexpr const provider_type type2{provider_type::s3};
 | 
			
		||||
  static constexpr const std::uint16_t port{0U};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct local_sia final {
 | 
			
		||||
  static constexpr const provider_type type{provider_type::sia};
 | 
			
		||||
  static constexpr const provider_type type2{provider_type::sia};
 | 
			
		||||
  static constexpr const std::uint16_t port{0U};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct remote_s3 final {
 | 
			
		||||
  static constexpr const provider_type type{provider_type::remote};
 | 
			
		||||
  static constexpr const provider_type type2{provider_type::s3};
 | 
			
		||||
  static constexpr const std::uint16_t port{0U};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct remote_sia final {
 | 
			
		||||
  static constexpr const provider_type type{provider_type::remote};
 | 
			
		||||
  static constexpr const provider_type type2{provider_type::sia};
 | 
			
		||||
  static constexpr const std::uint16_t port{0U};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct remote_linux_to_winfsp final {
 | 
			
		||||
  static constexpr const provider_type type{provider_type::remote};
 | 
			
		||||
  static constexpr const provider_type type2{provider_type::unknown};
 | 
			
		||||
  static constexpr const std::uint16_t port{40001U};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename provider_t> class fuse_test : public ::testing::Test {
 | 
			
		||||
@@ -113,7 +123,7 @@ protected:
 | 
			
		||||
 | 
			
		||||
          auto r_cfg = config->get_remote_mount();
 | 
			
		||||
          r_cfg.enable = true;
 | 
			
		||||
          r_cfg.api_port = 30000U;
 | 
			
		||||
          r_cfg.api_port = 40000U;
 | 
			
		||||
          config->set_remote_mount(r_cfg);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -160,7 +170,7 @@ protected:
 | 
			
		||||
 | 
			
		||||
          auto r_cfg = config->get_remote_mount();
 | 
			
		||||
          r_cfg.enable = true;
 | 
			
		||||
          r_cfg.api_port = 30000U;
 | 
			
		||||
          r_cfg.api_port = 40000U;
 | 
			
		||||
          config->set_remote_mount(r_cfg);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -178,7 +188,7 @@ protected:
 | 
			
		||||
      execute_mount(drive_args, mount_location);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const auto mount_remote = [&]() {
 | 
			
		||||
    const auto mount_remote = [&](std::uint16_t port = 40000U) {
 | 
			
		||||
      {
 | 
			
		||||
        mount_location2 = mount_location;
 | 
			
		||||
 | 
			
		||||
@@ -187,7 +197,8 @@ protected:
 | 
			
		||||
            {
 | 
			
		||||
                "fuse_test",
 | 
			
		||||
                app_config::get_provider_name(provider_t::type) + '_' +
 | 
			
		||||
                    app_config::get_provider_name(provider_t::type2),
 | 
			
		||||
                    app_config::get_provider_name(provider_t::type2) + '_' +
 | 
			
		||||
                    std::to_string(port),
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        mount_location = utils::path::combine(test_directory, {"mount"});
 | 
			
		||||
@@ -206,7 +217,7 @@ protected:
 | 
			
		||||
            "-dd",
 | 
			
		||||
            config2->get_data_directory(),
 | 
			
		||||
            "-rm",
 | 
			
		||||
            "localhost:30000",
 | 
			
		||||
            fmt::format("localhost:{}", port),
 | 
			
		||||
            mount_location,
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
@@ -233,6 +244,10 @@ protected:
 | 
			
		||||
        mount_sia();
 | 
			
		||||
      } break;
 | 
			
		||||
 | 
			
		||||
      case provider_type::unknown:
 | 
			
		||||
        mount_remote(provider_t::port);
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
      default:
 | 
			
		||||
        throw std::runtime_error("remote provider type is not implemented");
 | 
			
		||||
        return;
 | 
			
		||||
@@ -410,9 +425,15 @@ std::string fuse_test<provider_t>::mount_location;
 | 
			
		||||
template <typename provider_t>
 | 
			
		||||
std::string fuse_test<provider_t>::mount_location2;
 | 
			
		||||
 | 
			
		||||
// using fuse_provider_types = ::testing::Types<local_s3, remote_s3>;
 | 
			
		||||
#if defined(__linux__)
 | 
			
		||||
using fuse_provider_types =
 | 
			
		||||
    ::testing::Types<local_s3, remote_s3, local_sia, remote_sia>;
 | 
			
		||||
// using fuse_provider_types =
 | 
			
		||||
//     ::testing::Types<local_s3, remote_s3, local_sia, remote_sia,
 | 
			
		||||
//     remote_linux_to_winfsp>;
 | 
			
		||||
#else  // !defined(__linux__)
 | 
			
		||||
build fails here
 | 
			
		||||
#endif // defined(_WIN32)
 | 
			
		||||
} // namespace repertory
 | 
			
		||||
 | 
			
		||||
#endif // !defined(_WIN32)
 | 
			
		||||
 
 | 
			
		||||
@@ -45,21 +45,31 @@ namespace repertory {
 | 
			
		||||
struct local_s3 final {
 | 
			
		||||
  static constexpr const provider_type type{provider_type::s3};
 | 
			
		||||
  static constexpr const provider_type type2{provider_type::s3};
 | 
			
		||||
  static constexpr const std::uint16_t port{0U};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct local_sia final {
 | 
			
		||||
  static constexpr const provider_type type{provider_type::sia};
 | 
			
		||||
  static constexpr const provider_type type2{provider_type::sia};
 | 
			
		||||
  static constexpr const std::uint16_t port{0U};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct remote_s3 final {
 | 
			
		||||
  static constexpr const provider_type type{provider_type::remote};
 | 
			
		||||
  static constexpr const provider_type type2{provider_type::s3};
 | 
			
		||||
  static constexpr const std::uint16_t port{0U};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct remote_sia final {
 | 
			
		||||
  static constexpr const provider_type type{provider_type::remote};
 | 
			
		||||
  static constexpr const provider_type type2{provider_type::sia};
 | 
			
		||||
  static constexpr const std::uint16_t port{0U};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct remote_winfsp_to_linux final {
 | 
			
		||||
  static constexpr const provider_type type{provider_type::remote};
 | 
			
		||||
  static constexpr const provider_type type2{provider_type::unknown};
 | 
			
		||||
  static constexpr const std::uint16_t port{40001U};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename provider_t> class winfsp_test : public ::testing::Test {
 | 
			
		||||
@@ -102,7 +112,7 @@ protected:
 | 
			
		||||
 | 
			
		||||
          auto r_cfg = config->get_remote_mount();
 | 
			
		||||
          r_cfg.enable = true;
 | 
			
		||||
          r_cfg.api_port = 30000U;
 | 
			
		||||
          r_cfg.api_port = 40000U;
 | 
			
		||||
          config->set_remote_mount(r_cfg);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -144,7 +154,7 @@ protected:
 | 
			
		||||
 | 
			
		||||
          auto r_cfg = config->get_remote_mount();
 | 
			
		||||
          r_cfg.enable = true;
 | 
			
		||||
          r_cfg.api_port = 30000U;
 | 
			
		||||
          r_cfg.api_port = 40000U;
 | 
			
		||||
          config->set_remote_mount(r_cfg);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -160,13 +170,15 @@ protected:
 | 
			
		||||
      execute_mount(drive_args, mount_location);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const auto mount_remote = [&]() {
 | 
			
		||||
    const auto mount_remote = [&](std::uint16_t port = 40000U) {
 | 
			
		||||
      {
 | 
			
		||||
        auto test_directory = utils::path::combine(
 | 
			
		||||
            test::get_test_output_dir(),
 | 
			
		||||
            {
 | 
			
		||||
                "winfsp_test",
 | 
			
		||||
                app_config::get_provider_name(provider_type::remote),
 | 
			
		||||
                app_config::get_provider_name(provider_t::type) + '_' +
 | 
			
		||||
                    app_config::get_provider_name(provider_t::type2) + '_' +
 | 
			
		||||
                    std::to_string(port),
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        mount_location2 = mount_location;
 | 
			
		||||
@@ -184,7 +196,7 @@ protected:
 | 
			
		||||
            "-dd",
 | 
			
		||||
            config->get_data_directory(),
 | 
			
		||||
            "-rm",
 | 
			
		||||
            "localhost:30000",
 | 
			
		||||
            fmt::format("localhost:{}", port),
 | 
			
		||||
            mount_location,
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
@@ -211,6 +223,10 @@ protected:
 | 
			
		||||
        mount_sia();
 | 
			
		||||
      } break;
 | 
			
		||||
 | 
			
		||||
      case provider_type::unknown:
 | 
			
		||||
        mount_remote(provider_t::port);
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
      default:
 | 
			
		||||
        throw std::runtime_error("remote provider type is not implemented");
 | 
			
		||||
        return;
 | 
			
		||||
@@ -228,7 +244,9 @@ protected:
 | 
			
		||||
  static void TearDownTestCase() {
 | 
			
		||||
    if (provider_t::type == provider_type::remote) {
 | 
			
		||||
      execute_unmount(drive_args2, mount_location);
 | 
			
		||||
      if (provider_t::type2 != provider_type::unknown) {
 | 
			
		||||
        execute_unmount(drive_args, mount_location2);
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      execute_unmount(drive_args, mount_location);
 | 
			
		||||
    }
 | 
			
		||||
@@ -245,6 +263,7 @@ protected:
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static void execute_unmount(auto args, auto location) {
 | 
			
		||||
    std::this_thread::sleep_for(10s);
 | 
			
		||||
    auto unmounted{false};
 | 
			
		||||
 | 
			
		||||
    auto unmount_cmd =
 | 
			
		||||
@@ -282,7 +301,8 @@ std::string winfsp_test<provider_t>::mount_location2;
 | 
			
		||||
 | 
			
		||||
// using winfsp_provider_types = ::testing::Types<local_s3, remote_s3,
 | 
			
		||||
// local_sia, remote_sia>;
 | 
			
		||||
using winfsp_provider_types = ::testing::Types<local_s3, remote_s3>;
 | 
			
		||||
// using winfsp_provider_types = ::testing::Types<local_s3, remote_s3>;
 | 
			
		||||
using winfsp_provider_types = ::testing::Types<remote_winfsp_to_linux>;
 | 
			
		||||
} // namespace repertory
 | 
			
		||||
 | 
			
		||||
#endif // defined(_WIN32)
 | 
			
		||||
 
 | 
			
		||||
@@ -48,8 +48,8 @@ public:
 | 
			
		||||
    return api_error::success;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto check_parent_access(const std::string &,
 | 
			
		||||
                           int) const -> api_error override {
 | 
			
		||||
  auto check_parent_access(const std::string &, int) const
 | 
			
		||||
      -> api_error override {
 | 
			
		||||
    return api_error::success;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -84,8 +84,8 @@ public:
 | 
			
		||||
    return 0U;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto get_item_meta(const std::string &api_path,
 | 
			
		||||
                     api_meta_map &meta) const -> api_error override {
 | 
			
		||||
  auto get_item_meta(const std::string &api_path, api_meta_map &meta) const
 | 
			
		||||
      -> api_error override {
 | 
			
		||||
    meta = const_cast<mock_fuse_drive *>(this)->meta_[api_path];
 | 
			
		||||
    return api_error::success;
 | 
			
		||||
  }
 | 
			
		||||
@@ -124,8 +124,8 @@ public:
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto rename_file(const std::string &from_api_path,
 | 
			
		||||
                   const std::string &to_api_path,
 | 
			
		||||
                   bool overwrite) -> int override {
 | 
			
		||||
                   const std::string &to_api_path, bool overwrite)
 | 
			
		||||
      -> int override {
 | 
			
		||||
    const auto from_file_path =
 | 
			
		||||
        utils::path::combine(mount_location_, {from_api_path});
 | 
			
		||||
    const auto to_file_path =
 | 
			
		||||
@@ -152,6 +152,12 @@ public:
 | 
			
		||||
                     const std::string &value) override {
 | 
			
		||||
    meta_[api_path][key] = value;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void set_item_meta(const std::string &api_path,
 | 
			
		||||
                     const api_meta_map &meta) override {
 | 
			
		||||
    for (const auto &[key, value] : meta) {
 | 
			
		||||
      meta_[api_path][key] = value;
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
} // namespace repertory
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -128,7 +128,7 @@ std::atomic<std::uint64_t> app_config_test::idx{0U};
 | 
			
		||||
 | 
			
		||||
static void defaults_tests(const json &json_data, provider_type prov) {
 | 
			
		||||
  json json_defaults = {
 | 
			
		||||
      {JSON_API_PORT, app_config::default_rpc_port(prov)},
 | 
			
		||||
      {JSON_API_PORT, app_config::default_rpc_port()},
 | 
			
		||||
      {JSON_API_USER, std::string{REPERTORY}},
 | 
			
		||||
      {JSON_DOWNLOAD_TIMEOUT_SECS, default_download_timeout_secs},
 | 
			
		||||
      {JSON_DATABASE_TYPE, database_type::rocksdb},
 | 
			
		||||
@@ -185,9 +185,9 @@ static void defaults_tests(const json &json_data, provider_type prov) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  fmt::println("testing default|{}-{}", app_config::get_provider_name(prov),
 | 
			
		||||
               JSON_API_AUTH);
 | 
			
		||||
  ASSERT_EQ(std::size_t(default_api_auth_size),
 | 
			
		||||
            json_data.at(JSON_API_AUTH).get<std::string>().size());
 | 
			
		||||
               JSON_API_PASSWORD);
 | 
			
		||||
  ASSERT_EQ(std::size_t(default_api_password_size),
 | 
			
		||||
            json_data.at(JSON_API_PASSWORD).get<std::string>().size());
 | 
			
		||||
  for (const auto &[key, value] : json_defaults.items()) {
 | 
			
		||||
    fmt::println("testing default|{}-{}", app_config::get_provider_name(prov),
 | 
			
		||||
                 key);
 | 
			
		||||
@@ -216,11 +216,11 @@ static void common_tests(app_config &config, provider_type prov) {
 | 
			
		||||
  ASSERT_EQ(config.get_provider_type(), prov);
 | 
			
		||||
 | 
			
		||||
  std::map<std::string_view, std::function<void(app_config &)>> methods{
 | 
			
		||||
      {JSON_API_AUTH,
 | 
			
		||||
      {JSON_API_PASSWORD,
 | 
			
		||||
       [](app_config &cfg) {
 | 
			
		||||
         test_getter_setter(cfg, &app_config::get_api_auth,
 | 
			
		||||
                            &app_config::set_api_auth, "", "auth",
 | 
			
		||||
                            JSON_API_AUTH, "auth2");
 | 
			
		||||
         test_getter_setter(cfg, &app_config::get_api_password,
 | 
			
		||||
                            &app_config::set_api_password, "", "auth",
 | 
			
		||||
                            JSON_API_PASSWORD, "auth2");
 | 
			
		||||
       }},
 | 
			
		||||
      {JSON_API_PORT,
 | 
			
		||||
       [](app_config &cfg) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										198
									
								
								repertory/repertory_test/src/clean_json_test.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								repertory/repertory_test/src/clean_json_test.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,198 @@
 | 
			
		||||
/*
 | 
			
		||||
  Copyright <2018-2025> <scott.e.graves@protonmail.com>
 | 
			
		||||
 | 
			
		||||
  Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
  of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
  in the Software without restriction, including without limitation the rights
 | 
			
		||||
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
  copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
  furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
  The above copyright notice and this permission notice shall be included in all
 | 
			
		||||
  copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | 
			
		||||
  SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
#include "test_common.hpp"
 | 
			
		||||
 | 
			
		||||
#include "app_config.hpp"
 | 
			
		||||
#include "types/repertory.hpp"
 | 
			
		||||
 | 
			
		||||
namespace repertory {
 | 
			
		||||
TEST(clean_json_test, can_clean_values) {
 | 
			
		||||
  auto result = clean_json_value(JSON_API_PASSWORD, "moose");
 | 
			
		||||
  EXPECT_TRUE(result.empty());
 | 
			
		||||
 | 
			
		||||
  result = clean_json_value(
 | 
			
		||||
      fmt::format("{}.{}", JSON_ENCRYPT_CONFIG, JSON_ENCRYPTION_TOKEN),
 | 
			
		||||
      "moose");
 | 
			
		||||
  EXPECT_TRUE(result.empty());
 | 
			
		||||
 | 
			
		||||
  result = clean_json_value(
 | 
			
		||||
      fmt::format("{}.{}", JSON_HOST_CONFIG, JSON_API_PASSWORD), "moose");
 | 
			
		||||
  EXPECT_TRUE(result.empty());
 | 
			
		||||
 | 
			
		||||
  result = clean_json_value(
 | 
			
		||||
      fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_ENCRYPTION_TOKEN), "moose");
 | 
			
		||||
  EXPECT_TRUE(result.empty());
 | 
			
		||||
 | 
			
		||||
  result = clean_json_value(
 | 
			
		||||
      fmt::format("{}.{}", JSON_REMOTE_MOUNT, JSON_ENCRYPTION_TOKEN), "moose");
 | 
			
		||||
  EXPECT_TRUE(result.empty());
 | 
			
		||||
 | 
			
		||||
  result = clean_json_value(
 | 
			
		||||
      fmt::format("{}.{}", JSON_S3_CONFIG, JSON_ENCRYPTION_TOKEN), "moose");
 | 
			
		||||
  EXPECT_TRUE(result.empty());
 | 
			
		||||
 | 
			
		||||
  result = clean_json_value(
 | 
			
		||||
      fmt::format("{}.{}", JSON_S3_CONFIG, JSON_SECRET_KEY), "moose");
 | 
			
		||||
  EXPECT_TRUE(result.empty());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(clean_json_test, can_clean_encrypt_config) {
 | 
			
		||||
  auto dir =
 | 
			
		||||
      utils::path::combine(test::get_test_output_dir(), {
 | 
			
		||||
                                                            "clean_json_test",
 | 
			
		||||
                                                            "encrypt",
 | 
			
		||||
                                                        });
 | 
			
		||||
  app_config cfg(provider_type::encrypt, dir);
 | 
			
		||||
  cfg.set_value_by_name(JSON_API_PASSWORD, "moose");
 | 
			
		||||
  cfg.set_value_by_name(
 | 
			
		||||
      fmt::format("{}.{}", JSON_ENCRYPT_CONFIG, JSON_ENCRYPTION_TOKEN),
 | 
			
		||||
      "moose");
 | 
			
		||||
  cfg.set_value_by_name(
 | 
			
		||||
      fmt::format("{}.{}", JSON_REMOTE_MOUNT, JSON_ENCRYPTION_TOKEN), "moose");
 | 
			
		||||
 | 
			
		||||
  auto data = cfg.get_json();
 | 
			
		||||
  EXPECT_FALSE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
 | 
			
		||||
  EXPECT_FALSE(data.at(JSON_ENCRYPT_CONFIG)
 | 
			
		||||
                   .at(JSON_ENCRYPTION_TOKEN)
 | 
			
		||||
                   .get<std::string>()
 | 
			
		||||
                   .empty());
 | 
			
		||||
  EXPECT_FALSE(data.at(JSON_REMOTE_MOUNT)
 | 
			
		||||
                   .at(JSON_ENCRYPTION_TOKEN)
 | 
			
		||||
                   .get<std::string>()
 | 
			
		||||
                   .empty());
 | 
			
		||||
 | 
			
		||||
  clean_json_config(cfg.get_provider_type(), data);
 | 
			
		||||
  EXPECT_TRUE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
 | 
			
		||||
  EXPECT_TRUE(data.at(JSON_ENCRYPT_CONFIG)
 | 
			
		||||
                  .at(JSON_ENCRYPTION_TOKEN)
 | 
			
		||||
                  .get<std::string>()
 | 
			
		||||
                  .empty());
 | 
			
		||||
  EXPECT_TRUE(data.at(JSON_REMOTE_MOUNT)
 | 
			
		||||
                  .at(JSON_ENCRYPTION_TOKEN)
 | 
			
		||||
                  .get<std::string>()
 | 
			
		||||
                  .empty());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(clean_json_test, can_clean_remote_config) {
 | 
			
		||||
  auto dir =
 | 
			
		||||
      utils::path::combine(test::get_test_output_dir(), {
 | 
			
		||||
                                                            "clean_json_test",
 | 
			
		||||
                                                            "remote",
 | 
			
		||||
                                                        });
 | 
			
		||||
  app_config cfg(provider_type::remote, dir);
 | 
			
		||||
  cfg.set_value_by_name(JSON_API_PASSWORD, "moose");
 | 
			
		||||
  cfg.set_value_by_name(
 | 
			
		||||
      fmt::format("{}.{}", JSON_REMOTE_CONFIG, JSON_ENCRYPTION_TOKEN), "moose");
 | 
			
		||||
 | 
			
		||||
  auto data = cfg.get_json();
 | 
			
		||||
  EXPECT_FALSE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
 | 
			
		||||
  EXPECT_FALSE(data.at(JSON_REMOTE_CONFIG)
 | 
			
		||||
                   .at(JSON_ENCRYPTION_TOKEN)
 | 
			
		||||
                   .get<std::string>()
 | 
			
		||||
                   .empty());
 | 
			
		||||
 | 
			
		||||
  clean_json_config(cfg.get_provider_type(), data);
 | 
			
		||||
  EXPECT_TRUE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
 | 
			
		||||
  EXPECT_TRUE(data.at(JSON_REMOTE_CONFIG)
 | 
			
		||||
                  .at(JSON_ENCRYPTION_TOKEN)
 | 
			
		||||
                  .get<std::string>()
 | 
			
		||||
                  .empty());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(clean_json_test, can_clean_s3_config) {
 | 
			
		||||
  auto dir =
 | 
			
		||||
      utils::path::combine(test::get_test_output_dir(), {
 | 
			
		||||
                                                            "clean_json_test",
 | 
			
		||||
                                                            "s3",
 | 
			
		||||
                                                        });
 | 
			
		||||
  app_config cfg(provider_type::s3, dir);
 | 
			
		||||
  cfg.set_value_by_name(JSON_API_PASSWORD, "moose");
 | 
			
		||||
  cfg.set_value_by_name(
 | 
			
		||||
      fmt::format("{}.{}", JSON_REMOTE_MOUNT, JSON_ENCRYPTION_TOKEN), "moose");
 | 
			
		||||
  cfg.set_value_by_name(
 | 
			
		||||
      fmt::format("{}.{}", JSON_S3_CONFIG, JSON_ENCRYPTION_TOKEN), "moose");
 | 
			
		||||
  cfg.set_value_by_name(fmt::format("{}.{}", JSON_S3_CONFIG, JSON_SECRET_KEY),
 | 
			
		||||
                        "moose");
 | 
			
		||||
 | 
			
		||||
  auto data = cfg.get_json();
 | 
			
		||||
  EXPECT_FALSE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
 | 
			
		||||
  EXPECT_FALSE(data.at(JSON_REMOTE_MOUNT)
 | 
			
		||||
                   .at(JSON_ENCRYPTION_TOKEN)
 | 
			
		||||
                   .get<std::string>()
 | 
			
		||||
                   .empty());
 | 
			
		||||
  EXPECT_FALSE(data.at(JSON_S3_CONFIG)
 | 
			
		||||
                   .at(JSON_ENCRYPTION_TOKEN)
 | 
			
		||||
                   .get<std::string>()
 | 
			
		||||
                   .empty());
 | 
			
		||||
  EXPECT_FALSE(
 | 
			
		||||
      data.at(JSON_S3_CONFIG).at(JSON_SECRET_KEY).get<std::string>().empty());
 | 
			
		||||
 | 
			
		||||
  clean_json_config(cfg.get_provider_type(), data);
 | 
			
		||||
  EXPECT_TRUE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
 | 
			
		||||
  EXPECT_TRUE(data.at(JSON_REMOTE_MOUNT)
 | 
			
		||||
                  .at(JSON_ENCRYPTION_TOKEN)
 | 
			
		||||
                  .get<std::string>()
 | 
			
		||||
                  .empty());
 | 
			
		||||
  EXPECT_TRUE(data.at(JSON_S3_CONFIG)
 | 
			
		||||
                  .at(JSON_ENCRYPTION_TOKEN)
 | 
			
		||||
                  .get<std::string>()
 | 
			
		||||
                  .empty());
 | 
			
		||||
  EXPECT_TRUE(
 | 
			
		||||
      data.at(JSON_S3_CONFIG).at(JSON_SECRET_KEY).get<std::string>().empty());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(clean_json_test, can_clean_sia_config) {
 | 
			
		||||
  auto dir =
 | 
			
		||||
      utils::path::combine(test::get_test_output_dir(), {
 | 
			
		||||
                                                            "clean_json_test",
 | 
			
		||||
                                                            "sia",
 | 
			
		||||
                                                        });
 | 
			
		||||
  app_config cfg(provider_type::sia, dir);
 | 
			
		||||
  cfg.set_value_by_name(JSON_API_PASSWORD, "moose");
 | 
			
		||||
  cfg.set_value_by_name(
 | 
			
		||||
      fmt::format("{}.{}", JSON_HOST_CONFIG, JSON_API_PASSWORD), "moose");
 | 
			
		||||
  cfg.set_value_by_name(
 | 
			
		||||
      fmt::format("{}.{}", JSON_REMOTE_MOUNT, JSON_ENCRYPTION_TOKEN), "moose");
 | 
			
		||||
 | 
			
		||||
  auto data = cfg.get_json();
 | 
			
		||||
  EXPECT_FALSE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
 | 
			
		||||
  EXPECT_FALSE(data.at(JSON_HOST_CONFIG)
 | 
			
		||||
                   .at(JSON_API_PASSWORD)
 | 
			
		||||
                   .get<std::string>()
 | 
			
		||||
                   .empty());
 | 
			
		||||
  EXPECT_FALSE(data.at(JSON_REMOTE_MOUNT)
 | 
			
		||||
                   .at(JSON_ENCRYPTION_TOKEN)
 | 
			
		||||
                   .get<std::string>()
 | 
			
		||||
                   .empty());
 | 
			
		||||
 | 
			
		||||
  clean_json_config(cfg.get_provider_type(), data);
 | 
			
		||||
  EXPECT_TRUE(data.at(JSON_API_PASSWORD).get<std::string>().empty());
 | 
			
		||||
  EXPECT_TRUE(data.at(JSON_HOST_CONFIG)
 | 
			
		||||
                  .at(JSON_API_PASSWORD)
 | 
			
		||||
                  .get<std::string>()
 | 
			
		||||
                  .empty());
 | 
			
		||||
  EXPECT_TRUE(data.at(JSON_REMOTE_MOUNT)
 | 
			
		||||
                  .at(JSON_ENCRYPTION_TOKEN)
 | 
			
		||||
                  .get<std::string>()
 | 
			
		||||
                  .empty());
 | 
			
		||||
}
 | 
			
		||||
} // namespace repertory
 | 
			
		||||
@@ -62,13 +62,16 @@ TEST(lock_data_test, set_and_unset_mount_state) {
 | 
			
		||||
 | 
			
		||||
  json mount_state;
 | 
			
		||||
  EXPECT_TRUE(l.get_mount_state(mount_state));
 | 
			
		||||
 | 
			
		||||
  EXPECT_STREQ(R"({"Active":true,"Location":"C:","PID":99})",
 | 
			
		||||
               mount_state["Sia1"].dump().c_str());
 | 
			
		||||
               mount_state.dump().c_str());
 | 
			
		||||
 | 
			
		||||
  EXPECT_TRUE(l2.get_mount_state(mount_state));
 | 
			
		||||
  EXPECT_STREQ(R"({"Active":true,"Location":"D:","PID":97})",
 | 
			
		||||
               mount_state["Remote1"].dump().c_str());
 | 
			
		||||
               mount_state.dump().c_str());
 | 
			
		||||
 | 
			
		||||
  EXPECT_TRUE(l3.get_mount_state(mount_state));
 | 
			
		||||
  EXPECT_STREQ(R"({"Active":true,"Location":"E:","PID":96})",
 | 
			
		||||
               mount_state["Remote2"].dump().c_str());
 | 
			
		||||
               mount_state.dump().c_str());
 | 
			
		||||
 | 
			
		||||
  EXPECT_TRUE(l.set_mount_state(false, "C:", 99));
 | 
			
		||||
  EXPECT_TRUE(l2.set_mount_state(false, "D:", 98));
 | 
			
		||||
@@ -76,11 +79,15 @@ TEST(lock_data_test, set_and_unset_mount_state) {
 | 
			
		||||
 | 
			
		||||
  EXPECT_TRUE(l.get_mount_state(mount_state));
 | 
			
		||||
  EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
 | 
			
		||||
               mount_state["Sia1"].dump().c_str());
 | 
			
		||||
               mount_state.dump().c_str());
 | 
			
		||||
 | 
			
		||||
  EXPECT_TRUE(l2.get_mount_state(mount_state));
 | 
			
		||||
  EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
 | 
			
		||||
               mount_state["Remote1"].dump().c_str());
 | 
			
		||||
               mount_state.dump().c_str());
 | 
			
		||||
 | 
			
		||||
  EXPECT_TRUE(l3.get_mount_state(mount_state));
 | 
			
		||||
  EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
 | 
			
		||||
               mount_state["Remote2"].dump().c_str());
 | 
			
		||||
               mount_state.dump().c_str());
 | 
			
		||||
}
 | 
			
		||||
#else
 | 
			
		||||
TEST(lock_data_test, set_and_unset_mount_state) {
 | 
			
		||||
@@ -91,14 +98,13 @@ TEST(lock_data_test, set_and_unset_mount_state) {
 | 
			
		||||
  EXPECT_TRUE(l.get_mount_state(mount_state));
 | 
			
		||||
 | 
			
		||||
  EXPECT_STREQ(R"({"Active":true,"Location":"/mnt/1","PID":99})",
 | 
			
		||||
               mount_state["Sia1"].dump().c_str());
 | 
			
		||||
               mount_state.dump().c_str());
 | 
			
		||||
 | 
			
		||||
  EXPECT_TRUE(l.set_mount_state(false, "/mnt/1", 99));
 | 
			
		||||
 | 
			
		||||
  EXPECT_TRUE(l.get_mount_state(mount_state));
 | 
			
		||||
 | 
			
		||||
  EXPECT_STREQ(R"({"Active":false,"Location":"","PID":-1})",
 | 
			
		||||
               mount_state["Sia1"].dump().c_str());
 | 
			
		||||
               mount_state.dump().c_str());
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
} // namespace repertory
 | 
			
		||||
 
 | 
			
		||||
@@ -65,13 +65,16 @@ TYPED_TEST(winfsp_test, rename_fails_if_dest_exists_and_replace_is_false) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto dir_path{
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir_4"}),
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir_5"}),
 | 
			
		||||
  };
 | 
			
		||||
  auto file_path{
 | 
			
		||||
      utils::path::combine(dir_path, {"test_file_4"}),
 | 
			
		||||
      utils::path::combine(dir_path, {"test_file_5"}),
 | 
			
		||||
  };
 | 
			
		||||
  auto file_path2{
 | 
			
		||||
      utils::path::combine(dir_path, {"test_file2_4"}),
 | 
			
		||||
      utils::path::combine(dir_path, {"test_file2_5"}),
 | 
			
		||||
  };
 | 
			
		||||
  auto file_path3{
 | 
			
		||||
      utils::path::combine(dir_path, {"test_file3_5"}),
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
 | 
			
		||||
@@ -84,9 +87,16 @@ TYPED_TEST(winfsp_test, rename_fails_if_dest_exists_and_replace_is_false) {
 | 
			
		||||
 | 
			
		||||
  EXPECT_TRUE(::MoveFileExA(file_path.c_str(), file_path2.c_str(), 0));
 | 
			
		||||
 | 
			
		||||
  EXPECT_FALSE(::MoveFileExA(file_path.c_str(), file_path2.c_str(), 0));
 | 
			
		||||
  handle = ::CreateFileA(file_path3.c_str(), GENERIC_READ | GENERIC_WRITE,
 | 
			
		||||
                         FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
 | 
			
		||||
                         CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
 | 
			
		||||
  ASSERT_NE(INVALID_HANDLE_VALUE, handle);
 | 
			
		||||
  ::CloseHandle(handle);
 | 
			
		||||
 | 
			
		||||
  EXPECT_FALSE(::MoveFileExA(file_path3.c_str(), file_path2.c_str(), 0));
 | 
			
		||||
  EXPECT_EQ(ERROR_ALREADY_EXISTS, ::GetLastError());
 | 
			
		||||
 | 
			
		||||
  EXPECT_TRUE(::DeleteFileA(file_path3.c_str()));
 | 
			
		||||
  EXPECT_TRUE(::DeleteFileA(file_path2.c_str()));
 | 
			
		||||
  EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
 | 
			
		||||
}
 | 
			
		||||
@@ -97,13 +107,16 @@ TYPED_TEST(winfsp_test, rename_succeeds_if_dest_exists_and_replace_is_true) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto dir_path{
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir_4"}),
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir_6"}),
 | 
			
		||||
  };
 | 
			
		||||
  auto file_path{
 | 
			
		||||
      utils::path::combine(dir_path, {"test_file_4"}),
 | 
			
		||||
      utils::path::combine(dir_path, {"test_file_6"}),
 | 
			
		||||
  };
 | 
			
		||||
  auto file_path2{
 | 
			
		||||
      utils::path::combine(dir_path, {"test_file2_4"}),
 | 
			
		||||
      utils::path::combine(dir_path, {"test_file2_6"}),
 | 
			
		||||
  };
 | 
			
		||||
  auto file_path3{
 | 
			
		||||
      utils::path::combine(dir_path, {"test_file3_6"}),
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
 | 
			
		||||
@@ -116,34 +129,35 @@ TYPED_TEST(winfsp_test, rename_succeeds_if_dest_exists_and_replace_is_true) {
 | 
			
		||||
 | 
			
		||||
  EXPECT_TRUE(::MoveFileExA(file_path.c_str(), file_path2.c_str(), 0));
 | 
			
		||||
 | 
			
		||||
  handle = ::CreateFileA(file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
 | 
			
		||||
  handle = ::CreateFileA(file_path3.c_str(), GENERIC_READ | GENERIC_WRITE,
 | 
			
		||||
                         FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
 | 
			
		||||
                         CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
 | 
			
		||||
  ASSERT_NE(INVALID_HANDLE_VALUE, handle);
 | 
			
		||||
  ::CloseHandle(handle);
 | 
			
		||||
 | 
			
		||||
  EXPECT_TRUE(::MoveFileExA(file_path.c_str(), file_path2.c_str(),
 | 
			
		||||
  EXPECT_TRUE(::MoveFileExA(file_path3.c_str(), file_path2.c_str(),
 | 
			
		||||
                            MOVEFILE_REPLACE_EXISTING));
 | 
			
		||||
 | 
			
		||||
  EXPECT_TRUE(::DeleteFileA(file_path2.c_str()));
 | 
			
		||||
  EXPECT_TRUE(::RemoveDirectoryA(dir_path.c_str()));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TYPED_TEST(winfsp_test, rename_can_rename_dir_if_dest_does_not_exist) {
 | 
			
		||||
TYPED_TEST(winfsp_test, rename_dir_succeeds_if_dest_does_not_exist) {
 | 
			
		||||
  if (this->current_provider == provider_type::s3) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto dir_path{
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir_4"}),
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir_7"}),
 | 
			
		||||
  };
 | 
			
		||||
  auto dir_path2{
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir2_4"}),
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir2_7"}),
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
 | 
			
		||||
 | 
			
		||||
  EXPECT_TRUE(::MoveFileExA(dir_path.c_str(), dir_path2.c_str(), 0));
 | 
			
		||||
  fmt::println("error|{}", ::GetLastError());
 | 
			
		||||
 | 
			
		||||
  EXPECT_TRUE(::RemoveDirectoryA(dir_path2.c_str()));
 | 
			
		||||
}
 | 
			
		||||
@@ -154,10 +168,10 @@ TYPED_TEST(winfsp_test, rename_dir_fails_if_dest_exists_and_replace_is_false) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto dir_path{
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir_4"}),
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir_8"}),
 | 
			
		||||
  };
 | 
			
		||||
  auto dir_path2{
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir2_4"}),
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir2_8"}),
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
 | 
			
		||||
@@ -176,10 +190,10 @@ TYPED_TEST(winfsp_test, rename_dir_fails_if_dest_exists_and_replace_is_true) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto dir_path{
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir_4"}),
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir_9"}),
 | 
			
		||||
  };
 | 
			
		||||
  auto dir_path2{
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir2_4"}),
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir2_9"}),
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
 | 
			
		||||
@@ -200,13 +214,13 @@ TYPED_TEST(winfsp_test,
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto dir_path{
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir_4"}),
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir_10"}),
 | 
			
		||||
  };
 | 
			
		||||
  auto dir_path2{
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir2_4"}),
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir2_10"}),
 | 
			
		||||
  };
 | 
			
		||||
  auto file_path{
 | 
			
		||||
      utils::path::combine(dir_path, {"test_file_4"}),
 | 
			
		||||
      utils::path::combine(dir_path, {"test_file_10"}),
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
 | 
			
		||||
@@ -231,13 +245,13 @@ TYPED_TEST(winfsp_test,
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto dir_path{
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir_4"}),
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir_11"}),
 | 
			
		||||
  };
 | 
			
		||||
  auto dir_path2{
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir2_4"}),
 | 
			
		||||
      utils::path::combine(this->mount_location, {"test_dir2_11"}),
 | 
			
		||||
  };
 | 
			
		||||
  auto file_path{
 | 
			
		||||
      utils::path::combine(dir_path, {"test_file_4"}),
 | 
			
		||||
      utils::path::combine(dir_path, {"test_file_11"}),
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  ASSERT_TRUE(::CreateDirectoryA(dir_path.c_str(), nullptr));
 | 
			
		||||
 
 | 
			
		||||
@@ -50,10 +50,17 @@ TYPED_TEST(winfsp_test, volume_can_get_volume_info) {
 | 
			
		||||
            flags);
 | 
			
		||||
  EXPECT_EQ(255U, max_component_length);
 | 
			
		||||
  EXPECT_EQ(0U, serial_num);
 | 
			
		||||
  if (this->current_provider == provider_type::unknown) {
 | 
			
		||||
    EXPECT_STREQ(
 | 
			
		||||
        ("repertory_" + app_config::get_provider_name(provider_type::sia))
 | 
			
		||||
            .c_str(),
 | 
			
		||||
        volume_label.c_str());
 | 
			
		||||
  } else {
 | 
			
		||||
    EXPECT_STREQ(
 | 
			
		||||
        ("repertory_" + app_config::get_provider_name(this->current_provider))
 | 
			
		||||
            .c_str(),
 | 
			
		||||
        volume_label.c_str());
 | 
			
		||||
  }
 | 
			
		||||
  EXPECT_STREQ(this->mount_location.c_str(), fs_name.c_str());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,7 @@ if [ "${PROJECT_IS_MINGW}" == "1" ] && [ "${PROJECT_STATIC_LINK}" == "OFF" ]; th
 | 
			
		||||
    /mingw64/bin/libstdc++-6.dll
 | 
			
		||||
    /mingw64/bin/libwinpthread-1.dll
 | 
			
		||||
    /mingw64/bin/libzlib1.dll
 | 
			
		||||
    /mingw64/bin/libzstd.dll
 | 
			
		||||
    /mingw64/bin/zlib1.dll
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -38,14 +38,17 @@ BRANCH=$(git branch --show-current)
 | 
			
		||||
RELEASE=$(grep PROJECT_RELEASE_ITER= ./config.sh | sed s/PROJECT_RELEASE_ITER=//g)
 | 
			
		||||
popd
 | 
			
		||||
 | 
			
		||||
if [ "${BRANCH}" == "master" ] || [ "${BRANCH}" == "alpha" ] || [ "${BRANCH}" == "beta" ] || [ "${BRANCH}" == "rc" ]; then
 | 
			
		||||
if [ "${BRANCH}" == "master" ] || [ "${BRANCH}" == "alpha" ] ||
 | 
			
		||||
  [ "${BRANCH}" == "main" ] || [ "${BRANCH}" == "release" ] ||
 | 
			
		||||
  [ "${BRANCH}" == "beta" ] || [ "${BRANCH}" == "rc" ]; then
 | 
			
		||||
  DEST_DIR=${DEST_DIR}/${RELEASE}
 | 
			
		||||
else
 | 
			
		||||
elif [[ ${BRANCH} = *'-alpha-'* ]] || [[ ${BRANCH} = *'-beta-'* ]] ||
 | 
			
		||||
  [[ ${BRANCH} = *'-rc-'* ]] || [[ ${BRANCH} = *'-release-'* ]]; then
 | 
			
		||||
  DEST_DIR=${DEST_DIR}/nightly
 | 
			
		||||
else
 | 
			
		||||
  error_exit "skipping ${PROJECT_FILE_PART}" 0
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
echo ${DEST_DIR}
 | 
			
		||||
 | 
			
		||||
pushd "${DIST_DIR}"
 | 
			
		||||
if [ ! -f "./${PROJECT_OUT_FILE}" ]; then
 | 
			
		||||
  error_exit "failed to find file: ${PROJECT_OUT_FILE}" 1
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
if [ "${PROJECT_BUILD_ARCH}" == "aarch64" ]; then
 | 
			
		||||
if [ "${PROJECT_BUILD_ARCH}" == "aarch64" ] &&
 | 
			
		||||
  [ "${PROJECT_ENABLE_MULTIARCH_DOCKER}" == "1" ]; then
 | 
			
		||||
  docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,12 @@ PROJECT_IS_MINGW=$4
 | 
			
		||||
PROJECT_IS_MINGW_UNIX=$5
 | 
			
		||||
DISABLE_CREATE_DIRS=$6
 | 
			
		||||
 | 
			
		||||
PROJECT_FLUTTER_BASE_HREF="/"
 | 
			
		||||
 | 
			
		||||
if [ "${PROJECT_IS_MINGW}" != "1" ]; then
 | 
			
		||||
  PROJECT_IS_MINGW_UNIX=0
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
PROJECT_SOURCE_DIR=${PROJECT_SCRIPTS_DIR}/..
 | 
			
		||||
PROJECT_SOURCE_DIR=$(realpath "${PROJECT_SOURCE_DIR}")
 | 
			
		||||
 | 
			
		||||
@@ -306,9 +312,10 @@ export PROJECT_COMPANY_NAME
 | 
			
		||||
export PROJECT_COPYRIGHT
 | 
			
		||||
export PROJECT_DESC
 | 
			
		||||
export PROJECT_DIST_DIR
 | 
			
		||||
export PROJECT_ENABLE_WIN32_LONG_PATH_NAMES
 | 
			
		||||
export PROJECT_ENABLE_V2_ERRORS
 | 
			
		||||
export PROJECT_ENABLE_WIN32_LONG_PATH_NAMES
 | 
			
		||||
export PROJECT_FILE_PART
 | 
			
		||||
export PROJECT_FLUTTER_BASE_HREF
 | 
			
		||||
export PROJECT_GIT_REV
 | 
			
		||||
export PROJECT_IS_ALPINE
 | 
			
		||||
export PROJECT_IS_ARM64
 | 
			
		||||
@@ -361,6 +368,7 @@ echo "  Dist dir: ${PROJECT_DIST_DIR}"
 | 
			
		||||
echo "  Enable v2 errors: ${PROJECT_ENABLE_V2_ERRORS}"
 | 
			
		||||
echo "  External build root: ${PROJECT_EXTERNAL_BUILD_ROOT}"
 | 
			
		||||
echo "  File part: ${PROJECT_FILE_PART}"
 | 
			
		||||
echo "  Flutter base href: ${PROJECT_FLUTTER_BASE_HREF}"
 | 
			
		||||
echo "  Is ARM64: ${PROJECT_IS_ARM64}"
 | 
			
		||||
echo "  Is Alpine: ${PROJECT_IS_ALPINE}"
 | 
			
		||||
echo "  Is MINGW on Unix: ${PROJECT_IS_MINGW_UNIX}"
 | 
			
		||||
 
 | 
			
		||||
@@ -59,7 +59,7 @@ PROJECT_DOWNLOADS[PKG_CONFIG]="https://pkgconfig.freedesktop.org/releases/pkg-co
 | 
			
		||||
PROJECT_DOWNLOADS[PUGIXML]="https://github.com/zeux/pugixml/releases/download/v${PROJECT_VERSIONS[PUGIXML]}/pugixml-${PROJECT_VERSIONS[PUGIXML]}.tar.gz;pugixml-${PROJECT_VERSIONS[PUGIXML]}.tar.gz;3rd_party"
 | 
			
		||||
PROJECT_DOWNLOADS[ROCKSDB]="https://github.com/facebook/rocksdb/archive/refs/tags/v${PROJECT_VERSIONS[ROCKSDB]}.tar.gz;rocksdb-${PROJECT_VERSIONS[ROCKSDB]}.tar.gz;3rd_party"
 | 
			
		||||
PROJECT_DOWNLOADS[SPDLOG]="https://github.com/gabime/spdlog/archive/refs/tags/v${PROJECT_VERSIONS[SPDLOG]}.tar.gz;spdlog-${PROJECT_VERSIONS[SPDLOG]}.tar.gz;3rd_party"
 | 
			
		||||
PROJECT_DOWNLOADS[SQLITE]="https://www.sqlite.org/2024/sqlite-amalgamation-${PROJECT_VERSIONS[SQLITE]}.zip;sqlite-amalgamation-${PROJECT_VERSIONS[SQLITE]}.zip;3rd_party"
 | 
			
		||||
PROJECT_DOWNLOADS[SQLITE]="https://www.sqlite.org/2025/sqlite-amalgamation-${PROJECT_VERSIONS[SQLITE]}.zip;sqlite-amalgamation-${PROJECT_VERSIONS[SQLITE]}.zip;3rd_party"
 | 
			
		||||
PROJECT_DOWNLOADS[STDUUID]="https://github.com/mariusbancila/stduuid/archive/refs/tags/v${PROJECT_VERSIONS[STDUUID]}.tar.gz;stduuid-${PROJECT_VERSIONS[STDUUID]}.tar.gz;3rd_party"
 | 
			
		||||
PROJECT_DOWNLOADS[ZLIB]="https://github.com/madler/zlib/archive/refs/tags/v${PROJECT_VERSIONS[ZLIB]}.tar.gz;zlib-${PROJECT_VERSIONS[ZLIB]}.tar.gz;3rd_party/mingw64"
 | 
			
		||||
export PROJECT_DOWNLOADS
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,8 @@ fi
 | 
			
		||||
ln -sf "${PROJECT_BUILD_DIR}/build/compile_commands.json" "${PROJECT_SOURCE_DIR}/compile_commands.json"
 | 
			
		||||
 | 
			
		||||
pushd "${PROJECT_BUILD_DIR}"
 | 
			
		||||
export CMAKE_BUILD_PARALLEL_LEVEL=${NUM_JOBS}
 | 
			
		||||
 | 
			
		||||
cmake "${PROJECT_SOURCE_DIR}" \
 | 
			
		||||
  -G"Unix Makefiles" \
 | 
			
		||||
  -DPROJECT_COMPANY_NAME="${PROJECT_COMPANY_NAME}" \
 | 
			
		||||
@@ -34,10 +36,10 @@ if [ "${PROJECT_BUILD_CLEAN}" == "clean" ]; then
 | 
			
		||||
  popd
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
make -j${NUM_JOBS} || exit 1
 | 
			
		||||
make || exit 1
 | 
			
		||||
 | 
			
		||||
pushd build
 | 
			
		||||
make -j${NUM_JOBS} || exit 1
 | 
			
		||||
make -j${CMAKE_BUILD_PARALLEL_LEVEL} || exit 1
 | 
			
		||||
popd
 | 
			
		||||
popd
 | 
			
		||||
 | 
			
		||||
@@ -79,6 +81,11 @@ EOF
 | 
			
		||||
  fi
 | 
			
		||||
done
 | 
			
		||||
 | 
			
		||||
if [ -f "${PROJECT_SOURCE_DIR}/web/${PROJECT_NAME}/pubspec.yaml" ]; then
 | 
			
		||||
  rsync -av --progress "${PROJECT_SOURCE_DIR}/web/${PROJECT_NAME}/build/web/" \
 | 
			
		||||
    "${PROJECT_DIST_DIR}/web/"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
if [ "${PROJECT_IS_MINGW}" == "1" ]; then
 | 
			
		||||
  . "${PROJECT_SCRIPTS_DIR}/copy_mingw64_deps.sh" "$1" "$2" "$3" "$4" "$5"
 | 
			
		||||
fi
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										43
									
								
								scripts/make_flutter.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										43
									
								
								scripts/make_flutter.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
PROJECT_SCRIPTS_DIR=$(realpath "$0")
 | 
			
		||||
PROJECT_SCRIPTS_DIR=$(dirname "${PROJECT_SCRIPTS_DIR}")
 | 
			
		||||
. "${PROJECT_SCRIPTS_DIR}/env.sh" "$1" "$2" "$3" "$4" "$5"
 | 
			
		||||
 | 
			
		||||
if [ -f "${PROJECT_SOURCE_DIR}/web/${PROJECT_NAME}/pubspec.yaml" ]; then
 | 
			
		||||
  if [ "${PROJECT_IS_MINGW}" == "0" ] || [ "${PROJECT_IS_MINGW_UNIX}" == "1" ]; then
 | 
			
		||||
    FLUTTER_CONTAINER_NAME="${PROJECT_NAME}_flutter"
 | 
			
		||||
    FLUTTER_TAG_NAME="${PROJECT_NAME}:flutter"
 | 
			
		||||
 | 
			
		||||
    docker stop ${FLUTTER_CONTAINER_NAME}
 | 
			
		||||
    docker rm ${FLUTTER_CONTAINER_NAME}
 | 
			
		||||
 | 
			
		||||
    docker build "${PROJECT_SOURCE_DIR}/docker/x86_64/" \
 | 
			
		||||
      --build-arg UID=$(id -u) \
 | 
			
		||||
      --build-arg GID=$(id -g) \
 | 
			
		||||
      -t ${FLUTTER_TAG_NAME} \
 | 
			
		||||
      -f "${PROJECT_SOURCE_DIR}/docker/x86_64/flutter" || exit 1
 | 
			
		||||
 | 
			
		||||
    docker run -td \
 | 
			
		||||
      --name ${FLUTTER_CONTAINER_NAME} \
 | 
			
		||||
      -u $(id -u):$(id -g) \
 | 
			
		||||
      -v ${PROJECT_SOURCE_DIR}:${PROJECT_SOURCE_DIR}:rw,z \
 | 
			
		||||
      -w ${PROJECT_SOURCE_DIR}/web/${PROJECT_NAME} \
 | 
			
		||||
      ${FLUTTER_TAG_NAME} || exit 1
 | 
			
		||||
 | 
			
		||||
    docker exec \
 | 
			
		||||
      ${FLUTTER_CONTAINER_NAME} \
 | 
			
		||||
      /bin/bash -c \
 | 
			
		||||
      "flutter clean;flutter build web --base-href=${PROJECT_FLUTTER_BASE_HREF}" || exit 1
 | 
			
		||||
 | 
			
		||||
    docker stop ${FLUTTER_CONTAINER_NAME}
 | 
			
		||||
    docker rm ${FLUTTER_CONTAINER_NAME}
 | 
			
		||||
  else
 | 
			
		||||
    pushd "${PROJECT_SOURCE_DIR}/web/${PROJECT_NAME}"
 | 
			
		||||
    export MSYS_NO_PATHCONV=1
 | 
			
		||||
    flutter clean
 | 
			
		||||
    flutter build web --base-href=${PROJECT_FLUTTER_BASE_HREF} || exit 1
 | 
			
		||||
    export MSYS_NO_PATHCONV=0
 | 
			
		||||
    popd
 | 
			
		||||
  fi
 | 
			
		||||
fi
 | 
			
		||||
@@ -9,6 +9,8 @@ if [ -f "${PROJECT_SCRIPTS_DIR}/cleanup.sh" ]; then
 | 
			
		||||
  rm ${PROJECT_SCRIPTS_DIR}/cleanup.*
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
"${PROJECT_SOURCE_DIR}/scripts/make_flutter.sh" "${PROJECT_BUILD_ARCH}" "${PROJECT_CMAKE_BUILD_TYPE}" "${PROJECT_BUILD_CLEAN}" 0 0 || exit 1
 | 
			
		||||
 | 
			
		||||
if [ "${PROJECT_REQUIRE_ALPINE}" == "ON" ] || [ "${PROJECT_IS_ARM64}" == "1" ]; then
 | 
			
		||||
  DOCKER_NAME=alpine
 | 
			
		||||
  DOCKER_CONTAINER=${PROJECT_NAME}_${DOCKER_NAME}_${PROJECT_BUILD_ARCH}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,5 +12,7 @@ pushd "%~dp0%"
 | 
			
		||||
    call cleanup.cmd "%ARG1%" "%ARG2%" "%ARG3%" 1 0
 | 
			
		||||
    del cleanup.*
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  call mingw64 -no-start ./make_flutter.sh "%ARG1%" "%ARG2%" "%ARG3%" 1 0 || exit 1
 | 
			
		||||
  call mingw64 -no-start ./make_common.sh "%ARG1%" "%ARG2%" "%ARG3%" 1 0 || exit 1
 | 
			
		||||
popd
 | 
			
		||||
 
 | 
			
		||||
@@ -1,32 +1,32 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
declare -A PROJECT_VERSIONS
 | 
			
		||||
PROJECT_VERSIONS[BINUTILS]="2.43"
 | 
			
		||||
PROJECT_VERSIONS[BINUTILS]="2.44"
 | 
			
		||||
PROJECT_VERSIONS[BOOST2_MAJOR]="1"
 | 
			
		||||
PROJECT_VERSIONS[BOOST2_MINOR]="76"
 | 
			
		||||
PROJECT_VERSIONS[BOOST2_PATCH]="0"
 | 
			
		||||
PROJECT_VERSIONS[BOOST_MAJOR]="1"
 | 
			
		||||
PROJECT_VERSIONS[BOOST_MINOR]="87"
 | 
			
		||||
PROJECT_VERSIONS[BOOST_MINOR]="88"
 | 
			
		||||
PROJECT_VERSIONS[BOOST_PATCH]="0"
 | 
			
		||||
PROJECT_VERSIONS[CPP_HTTPLIB]="0.18.1"
 | 
			
		||||
PROJECT_VERSIONS[CURL]="8.11.0"
 | 
			
		||||
PROJECT_VERSIONS[CURL2]="8_11_0"
 | 
			
		||||
PROJECT_VERSIONS[EXPAT]="2.6.4"
 | 
			
		||||
PROJECT_VERSIONS[EXPAT2]="2_6_4"
 | 
			
		||||
PROJECT_VERSIONS[CPP_HTTPLIB]="0.20.0"
 | 
			
		||||
PROJECT_VERSIONS[CURL]="8.13.0"
 | 
			
		||||
PROJECT_VERSIONS[CURL2]="8_13_0"
 | 
			
		||||
PROJECT_VERSIONS[EXPAT]="2.7.1"
 | 
			
		||||
PROJECT_VERSIONS[EXPAT2]="2_7_1"
 | 
			
		||||
PROJECT_VERSIONS[GCC]="14.2.0"
 | 
			
		||||
PROJECT_VERSIONS[GTEST]="1.15.2"
 | 
			
		||||
PROJECT_VERSIONS[GTEST]="1.16.0"
 | 
			
		||||
PROJECT_VERSIONS[ICU]="76-1"
 | 
			
		||||
PROJECT_VERSIONS[JSON]="3.11.3"
 | 
			
		||||
PROJECT_VERSIONS[JSON]="3.12.0"
 | 
			
		||||
PROJECT_VERSIONS[LIBSODIUM]="1.0.20"
 | 
			
		||||
PROJECT_VERSIONS[MESA]="23.3.3"
 | 
			
		||||
PROJECT_VERSIONS[MINGW]="12.0.0"
 | 
			
		||||
PROJECT_VERSIONS[OPENSSL]="3.4.0"
 | 
			
		||||
PROJECT_VERSIONS[OPENSSL]="3.5.0"
 | 
			
		||||
PROJECT_VERSIONS[PKG_CONFIG]="0.29.2"
 | 
			
		||||
PROJECT_VERSIONS[PUGIXML]="1.14"
 | 
			
		||||
PROJECT_VERSIONS[ROCKSDB]="9.7.4"
 | 
			
		||||
PROJECT_VERSIONS[SPDLOG]="1.15.0"
 | 
			
		||||
PROJECT_VERSIONS[SQLITE]="3460100"
 | 
			
		||||
PROJECT_VERSIONS[SQLITE2]="3.46.1"
 | 
			
		||||
PROJECT_VERSIONS[PUGIXML]="1.15"
 | 
			
		||||
PROJECT_VERSIONS[ROCKSDB]="10.0.1"
 | 
			
		||||
PROJECT_VERSIONS[SPDLOG]="1.15.2"
 | 
			
		||||
PROJECT_VERSIONS[SQLITE]="3490100"
 | 
			
		||||
PROJECT_VERSIONS[SQLITE2]="3.49.1"
 | 
			
		||||
PROJECT_VERSIONS[STDUUID]="1.2.3"
 | 
			
		||||
PROJECT_VERSIONS[ZLIB]="1.3.1"
 | 
			
		||||
export PROJECT_VERSIONS
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/boost_1_87_0.tar.gz
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/boost_1_87_0.tar.gz
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								support/3rd_party/boost_1_87_0.tar.gz.sha256
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								support/3rd_party/boost_1_87_0.tar.gz.sha256
									
									
									
									
										vendored
									
									
								
							@@ -1 +0,0 @@
 | 
			
		||||
f55c340aa49763b1925ccf02b2e83f35fdcf634c9d5164a2acb87540173c741d *boost_1_87_0.tar.gz
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/boost_1_88_0.tar.gz
									
									LFS
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/boost_1_88_0.tar.gz
									
									LFS
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								support/3rd_party/boost_1_88_0.tar.gz.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								support/3rd_party/boost_1_88_0.tar.gz.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
3621533e820dcab1e8012afd583c0c73cf0f77694952b81352bf38c1488f9cb4  boost_1_88_0.tar.gz
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/cpp-httplib-0.18.1.tar.gz
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/cpp-httplib-0.18.1.tar.gz
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -1 +0,0 @@
 | 
			
		||||
405abd8170f2a446fc8612ac635d0db5947c0d2e156e32603403a4496255ff00 *cpp-httplib-0.18.1.tar.gz
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/cpp-httplib-0.20.0.tar.gz
									
									LFS
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/cpp-httplib-0.20.0.tar.gz
									
									LFS
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								support/3rd_party/cpp-httplib-0.20.0.tar.gz.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								support/3rd_party/cpp-httplib-0.20.0.tar.gz.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
18064587e0cc6a0d5d56d619f4cbbcaba47aa5d84d86013abbd45d95c6653866  cpp-httplib-0.20.0.tar.gz
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/curl-8.11.0.tar.gz
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/curl-8.11.0.tar.gz
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								support/3rd_party/curl-8.11.0.tar.gz.sha256
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								support/3rd_party/curl-8.11.0.tar.gz.sha256
									
									
									
									
										vendored
									
									
								
							@@ -1 +0,0 @@
 | 
			
		||||
5a231145114589491fc52da118f9c7ef8abee885d1cb1ced99c7290e9a352f07 *curl-8.11.0.tar.gz
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/curl-8.13.0.tar.gz
									
									LFS
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/curl-8.13.0.tar.gz
									
									LFS
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								support/3rd_party/curl-8.13.0.tar.gz.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								support/3rd_party/curl-8.13.0.tar.gz.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
ccc5ba45d9f5320c70ffb24e5411b66ba55ea1f333bf78be0963ed90a9328699  curl-8.13.0.tar.gz
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/googletest-1.15.2.tar.gz
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/googletest-1.15.2.tar.gz
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/googletest-1.16.0.tar.gz
									
									LFS
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/googletest-1.16.0.tar.gz
									
									LFS
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								support/3rd_party/googletest-1.16.0.tar.gz.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								support/3rd_party/googletest-1.16.0.tar.gz.sha256
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
78c676fc63881529bf97bf9d45948d905a66833fbfa5318ea2cd7478cb98f399 *googletest-1.16.0.tar.gz
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/json-3.11.3.tar.gz
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/json-3.11.3.tar.gz
									
									
									
									
										vendored
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										1
									
								
								support/3rd_party/json-3.11.3.tar.gz.sha256
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								support/3rd_party/json-3.11.3.tar.gz.sha256
									
									
									
									
										vendored
									
									
								
							@@ -1 +0,0 @@
 | 
			
		||||
0d8ef5af7f9794e3263480193c491549b2ba6cc74bb018906202ada498a79406  json-3.11.3.tar.gz
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								support/3rd_party/json-3.12.0.tar.gz
									
									LFS
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								support/3rd_party/json-3.12.0.tar.gz
									
									LFS
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user