aboutsummaryrefslogtreecommitdiff
path: root/fstest/testserver/init.d
diff options
context:
space:
mode:
authorMichael Tews <michael@tews.dev>2026-03-06 10:58:19 +0100
committerMichael Tews <michael@tews.dev>2026-03-12 15:23:26 +0100
commitce94f2d69a5f1aab1fc8fc2947f0a6cfd81bb4d1 (patch)
tree4ae72baff8af7a73d46ccd544463aac3f638df39 /fstest/testserver/init.d
parenta59763b4ff8c5728401232a696dfc8a725cf4e02 (diff)
test: adds fstest from rclone
Signed-off-by: Michael Tews <michael@tews.dev>
Diffstat (limited to 'fstest/testserver/init.d')
-rw-r--r--fstest/testserver/init.d/PORTS.md49
-rw-r--r--fstest/testserver/init.d/README.md48
-rwxr-xr-xfstest/testserver/init.d/TestFTPProftpd25
-rwxr-xr-xfstest/testserver/init.d/TestFTPPureftpd29
-rwxr-xr-xfstest/testserver/init.d/TestFTPRclone22
-rwxr-xr-xfstest/testserver/init.d/TestFTPVsftpd26
-rwxr-xr-xfstest/testserver/init.d/TestFTPVsftpdTLS26
-rwxr-xr-xfstest/testserver/init.d/TestHdfs31
-rwxr-xr-xfstest/testserver/init.d/TestS3Exaba30
-rwxr-xr-xfstest/testserver/init.d/TestS3Minio27
-rwxr-xr-xfstest/testserver/init.d/TestS3MinioEdge27
-rwxr-xr-xfstest/testserver/init.d/TestS3Rclone22
-rwxr-xr-xfstest/testserver/init.d/TestSFTPOpenssh26
-rwxr-xr-xfstest/testserver/init.d/TestSFTPRclone22
-rwxr-xr-xfstest/testserver/init.d/TestSFTPRcloneSSH67
-rwxr-xr-xfstest/testserver/init.d/TestSMB33
-rwxr-xr-xfstest/testserver/init.d/TestSMBKerberos84
-rwxr-xr-xfstest/testserver/init.d/TestSMBKerberosCcache85
-rwxr-xr-xfstest/testserver/init.d/TestSeafile72
-rwxr-xr-xfstest/testserver/init.d/TestSeafileEncrypted65
-rwxr-xr-xfstest/testserver/init.d/TestSeafileV648
-rwxr-xr-xfstest/testserver/init.d/TestSia55
-rwxr-xr-xfstest/testserver/init.d/TestSwiftAIO25
-rwxr-xr-xfstest/testserver/init.d/TestSwiftAIO.d/remakerings46
-rwxr-xr-xfstest/testserver/init.d/TestSwiftAIOsegments26
-rwxr-xr-xfstest/testserver/init.d/TestWebdavInfiniteScale46
-rwxr-xr-xfstest/testserver/init.d/TestWebdavNextcloud29
-rwxr-xr-xfstest/testserver/init.d/TestWebdavOwncloud33
-rwxr-xr-xfstest/testserver/init.d/TestWebdavRclone22
-rw-r--r--fstest/testserver/init.d/docker.bash22
-rw-r--r--fstest/testserver/init.d/rclone-serve.bash42
-rw-r--r--fstest/testserver/init.d/run.bash101
-rw-r--r--fstest/testserver/init.d/seafile/docker-compose.yml31
33 files changed, 1342 insertions, 0 deletions
diff --git a/fstest/testserver/init.d/PORTS.md b/fstest/testserver/init.d/PORTS.md
new file mode 100644
index 0000000..fc955a2
--- /dev/null
+++ b/fstest/testserver/init.d/PORTS.md
@@ -0,0 +1,49 @@
+# Ports for tests
+
+All these tests need to run on a different port.
+
+They should be bound to localhost so they are not accessible externally.
+
+| Port | Test |
+|:-----:|:----:|
+| 88 | TestHdfs |
+| 750 | TestHdfs |
+| 8020 | TestHdfs |
+| 8086 | TestSeafileV6 |
+| 8087 | TestSeafile |
+| 8088 | TestSeafileEncrypted |
+| 9866 | TestHdfs |
+| 28620 | TestWebdavRclone |
+| 28621 | TestSFTPRclone |
+| 28622 | TestFTPRclone |
+| 28623 | TestSFTPRcloneSSH |
+| 28624 | TestS3Rclone |
+| 28625 | TestS3Minio |
+| 28626 | TestS3MinioEdge |
+| 28627 | TestSFTPOpenssh |
+| 28628 | TestSwiftAIO |
+| 28629 | TestWebdavNextcloud |
+| 28630 | TestSMB |
+| 28631 | TestFTPProftpd |
+| 28632 | TestSwiftAIOsegments |
+| 28633 | TestSMBKerberos |
+| 28634 | TestSMBKerberos |
+| 28635 | TestS3Exaba |
+| 28636 | TestS3Exaba |
+| 28637 | TestSMBKerberosCcache |
+| 28638 | TestSMBKerberosCcache |
+| 28639 | TestWebdavInfiniteScale |
+| 38081 | TestWebdavOwncloud |
+
+## Non localhost tests
+
+All these use `$(docker_ip)` which means they don't work on macOS or
+Windows. It is proabably possible to make them work with some effort
+but will require port forwarding a range of ports and configuring the
+FTP server to only use that range of ports. The FTP server will likely
+need know it is behind a NAT so it advertises the correct external IP.
+
+- TestFTPProftpd
+- TestFTPPureftpd
+- TestFTPVsftpd
+- TestFTPVsftpdTLS
diff --git a/fstest/testserver/init.d/README.md b/fstest/testserver/init.d/README.md
new file mode 100644
index 0000000..c9acd92
--- /dev/null
+++ b/fstest/testserver/init.d/README.md
@@ -0,0 +1,48 @@
+This directory contains scripts to start and stop servers for testing.
+
+The commands are named after the remotes in use. They are executable
+files with the following parameters:
+
+ start - starts the server if not running
+ stop - stops the server if nothing is using it
+ status - returns non-zero exit code if the server is not running
+ reset - stops the server and resets any reference counts
+
+These will be called automatically by test_all if that remote is
+required.
+
+When start is run it should output config parameters for that remote.
+If a `_connect` parameter is output then that will be used for a
+connection test. For example if `_connect=127.0.0.1:80` then a TCP
+connection will be made to `127.0.0.1:80` and only when that succeeds
+will the test continue.
+
+If in addition to `_connect`, `_connect_delay=5s` is also present then
+after the connection succeeds rclone will wait `5s` before continuing.
+This is for servers that aren't quite ready even though they have
+opened their TCP ports.
+
+## Writing new scripts
+
+A docker based server or an `rclone serve` based server should be easy
+to write. Look at one of the examples.
+
+`run.bash` contains boilerplate to be included in a bash script for
+interpreting the command line parameters. This does reference counting
+to ensure multiple copies of the server aren't running at once.
+Including this is mandatory. It will call your `start()`, `stop()` and
+`status()` functions.
+
+`docker.bash` contains library functions to help with docker
+implementations. It contains implementations of `stop()` and
+`status()` so all you have to do is write a `start()` function.
+
+`rclone-serve.bash` contains functions to help with `rclone serve`
+based implementations. It contains implementations of `stop()` and
+`status()` so all you have to do is write a `start()` function which
+should call the `run()` function provided.
+
+Any external TCP or UDP ports used should be unique as any of the
+servers might be running together. So please create a new line in the
+[PORTS](PORTS.md) file to allocate your server a port. Bind any ports
+to localhost so they aren't accessible externally.
diff --git a/fstest/testserver/init.d/TestFTPProftpd b/fstest/testserver/init.d/TestFTPProftpd
new file mode 100755
index 0000000..029909a
--- /dev/null
+++ b/fstest/testserver/init.d/TestFTPProftpd
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=proftpd
+USER=rclone
+PASS=RaidedBannedPokes5
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ docker run --rm -d --name $NAME \
+ -e "FTP_USERNAME=rclone" \
+ -e "FTP_PASSWORD=$PASS" \
+ hauptmedia/proftpd
+
+ echo type=ftp
+ echo host=$(docker_ip)
+ echo user=$USER
+ echo pass=$(rclone obscure $PASS)
+ echo encoding=Asterisk,Ctl,Dot,Slash
+ echo _connect=$(docker_ip):21
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestFTPPureftpd b/fstest/testserver/init.d/TestFTPPureftpd
new file mode 100755
index 0000000..69c9285
--- /dev/null
+++ b/fstest/testserver/init.d/TestFTPPureftpd
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=pureftpd
+USER=rclone
+PASS=AcridSpiesBooks2
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ docker run --rm -d --name $NAME \
+ -e "FTP_USER_NAME=rclone" \
+ -e "FTP_USER_PASS=$PASS" \
+ -e "FTP_USER_HOME=/data" \
+ -e "FTP_MAX_CLIENTS=50" \
+ -e "FTP_MAX_CONNECTIONS=50" \
+ -e "FTP_PASSIVE_PORTS=30000:40000" \
+ stilliard/pure-ftpd
+
+ echo type=ftp
+ echo host=$(docker_ip)
+ echo user=$USER
+ echo pass=$(rclone obscure $PASS)
+ echo encoding=BackSlash,Ctl,Del,Dot,RightSpace,Slash,SquareBracket
+ echo _connect=$(docker_ip):21
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestFTPRclone b/fstest/testserver/init.d/TestFTPRclone
new file mode 100755
index 0000000..85ad26a
--- /dev/null
+++ b/fstest/testserver/init.d/TestFTPRclone
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=rclone-serve-ftp
+USER=rclone
+PASS=FuddleIdlingJell5
+IP=127.0.0.1
+PORT=28622
+
+start() {
+ run rclone serve ftp --user $USER --pass $PASS --addr ${IP}:${PORT} ${DATADIR}
+
+ echo type=ftp
+ echo host=${IP}
+ echo port=$PORT
+ echo user=$USER
+ echo pass=$(rclone obscure $PASS)
+ echo _connect=${IP}:${PORT}
+}
+
+. $(dirname "$0")/rclone-serve.bash
diff --git a/fstest/testserver/init.d/TestFTPVsftpd b/fstest/testserver/init.d/TestFTPVsftpd
new file mode 100755
index 0000000..d33dcf0
--- /dev/null
+++ b/fstest/testserver/init.d/TestFTPVsftpd
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=vsftpd
+USER=rclone
+PASS=TiffedRestedSian4
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ docker run --rm -d --name $NAME \
+ -e "FTP_USER=rclone" \
+ -e "FTP_PASS=$PASS" \
+ fauria/vsftpd
+
+ echo type=ftp
+ echo host=$(docker_ip)
+ echo user=$USER
+ echo pass=$(rclone obscure $PASS)
+ echo writing_mdtm=true
+ echo encoding=Ctl,LeftPeriod,Slash
+ echo _connect=$(docker_ip):21
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestFTPVsftpdTLS b/fstest/testserver/init.d/TestFTPVsftpdTLS
new file mode 100755
index 0000000..ebcd3b0
--- /dev/null
+++ b/fstest/testserver/init.d/TestFTPVsftpdTLS
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=vsftpdtls
+USER=rclone
+PASS=TiffedRestedSian4
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ docker run --rm -d --name $NAME \
+ -e "FTP_USER=rclone" \
+ -e "FTP_PASS=$PASS" \
+ rclone/vsftpd
+
+ echo type=ftp
+ echo host=$(docker_ip)
+ echo user=$USER
+ echo pass=$(rclone obscure $PASS)
+ echo writing_mdtm=true
+ echo encoding=Ctl,LeftPeriod,Slash
+ echo _connect=$(docker_ip):21
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestHdfs b/fstest/testserver/init.d/TestHdfs
new file mode 100755
index 0000000..26aea1a
--- /dev/null
+++ b/fstest/testserver/init.d/TestHdfs
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=rclone-hdfs
+KERBEROS=${KERBEROS-"false"}
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ docker run --rm -d --name "rclone-hdfs" \
+ --hostname "rclone-hdfs" \
+ -e "KERBEROS=$KERBEROS" \
+ -p 127.0.0.1:9866:9866 \
+ -p 127.0.0.1:8020:8020 \
+ -p 127.0.0.1:750:750 \
+ -p 127.0.0.1:88:88 \
+ rclone/test-hdfs
+ sleep 30
+
+ if [ $KERBEROS = "true" ]; then
+ docker cp rclone-hdfs:/tmp/krb5cc_0 /tmp/krb5cc_`id -u`
+ fi
+
+ echo type=hdfs
+ echo namenode=127.0.0.1:8020
+ echo username=root
+ echo _connect=127.0.0.1:8020
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestS3Exaba b/fstest/testserver/init.d/TestS3Exaba
new file mode 100755
index 0000000..c9a7f92
--- /dev/null
+++ b/fstest/testserver/init.d/TestS3Exaba
@@ -0,0 +1,30 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=exaba
+USER="Use the webui to find the access_key_id"
+PASS="Use the webui to find the secret_access_key"
+PORT=28635
+WEBUIPORT=28636
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ docker run --rm -d --name $NAME \
+ -e "CLUSTER_NAME=rclone" \
+ -e "CLUSTER_SIZE_GB=20" \
+ -p 127.0.0.1:${PORT}:9000 \
+ -p 127.0.0.1:${WEBUIPORT}:9006 \
+ exaba/exaba
+
+ echo type=s3
+ echo provider=Exaba
+ echo access_key_id=$USER
+ echo secret_access_key=$PASS
+ echo endpoint=http://127.0.0.1:${PORT}/
+ echo webui=http://127.0.0.1:${WEBUIPORT}/
+ echo _connect=127.0.0.1:${PORT}
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestS3Minio b/fstest/testserver/init.d/TestS3Minio
new file mode 100755
index 0000000..b4d3dde
--- /dev/null
+++ b/fstest/testserver/init.d/TestS3Minio
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=minio
+USER=rclone
+PASS=AxedBodedGinger7
+PORT=28625
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ docker run --rm -d --name $NAME \
+ -e "MINIO_ACCESS_KEY=$USER" \
+ -e "MINIO_SECRET_KEY=$PASS" \
+ -p 127.0.0.1:${PORT}:9000 \
+ minio/minio server /data
+
+ echo type=s3
+ echo provider=Minio
+ echo access_key_id=$USER
+ echo secret_access_key=$PASS
+ echo endpoint=http://127.0.0.1:${PORT}/
+ echo _connect=127.0.0.1:${PORT}
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestS3MinioEdge b/fstest/testserver/init.d/TestS3MinioEdge
new file mode 100755
index 0000000..399ec7f
--- /dev/null
+++ b/fstest/testserver/init.d/TestS3MinioEdge
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=minio-edge
+USER=rclone
+PASS=DeniseOxygenEiffel4
+PORT=28626
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ docker run --rm -d --name $NAME \
+ -e "MINIO_ACCESS_KEY=$USER" \
+ -e "MINIO_SECRET_KEY=$PASS" \
+ -p 127.0.0.1:${PORT}:9000 \
+ minio/minio:edge server /data
+
+ echo type=s3
+ echo provider=Minio
+ echo access_key_id=$USER
+ echo secret_access_key=$PASS
+ echo endpoint=http://127.0.0.1:${PORT}/
+ echo _connect=127.0.0.1:${PORT}
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestS3Rclone b/fstest/testserver/init.d/TestS3Rclone
new file mode 100755
index 0000000..c336322
--- /dev/null
+++ b/fstest/testserver/init.d/TestS3Rclone
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=rclone-serve-s3
+ACCESS_KEY_ID=rclone
+SECRET_ACCESS_KEY=JoltRogueVerde5
+IP=127.0.0.1
+PORT=28624
+
+start() {
+ run rclone serve s3 --auth-key ${ACCESS_KEY_ID},${SECRET_ACCESS_KEY} --addr ${IP}:${PORT} ${DATADIR}
+
+ echo type=s3
+ echo provider=Rclone
+ echo endpoint=http://${IP}:${PORT}/
+ echo access_key_id=${ACCESS_KEY_ID}
+ echo secret_access_key=${SECRET_ACCESS_KEY}
+ echo _connect=${IP}:${PORT}
+}
+
+. $(dirname "$0")/rclone-serve.bash
diff --git a/fstest/testserver/init.d/TestSFTPOpenssh b/fstest/testserver/init.d/TestSFTPOpenssh
new file mode 100755
index 0000000..91a9c9a
--- /dev/null
+++ b/fstest/testserver/init.d/TestSFTPOpenssh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=rclone-sftp-openssh
+USER=rclone
+PASS=password
+PORT=28627
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ docker run --rm -d --name ${NAME} \
+ -p 127.0.0.1:${PORT}:22 \
+ rclone/test-sftp-openssh
+
+ echo type=sftp
+ echo host=127.0.0.1
+ echo port=$PORT
+ echo user=$USER
+ echo pass=$(rclone obscure $PASS)
+ echo copy_is_hardlink=true
+ echo _connect=127.0.0.1:${PORT}
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestSFTPRclone b/fstest/testserver/init.d/TestSFTPRclone
new file mode 100755
index 0000000..f553112
--- /dev/null
+++ b/fstest/testserver/init.d/TestSFTPRclone
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=rclone-serve-sftp
+USER=rclone
+PASS=CranesBallotDorsey5
+IP=127.0.0.1
+PORT=28621
+
+start() {
+ run rclone serve sftp --user $USER --pass $PASS --addr ${IP}:${PORT} ${DATADIR}
+
+ echo type=sftp
+ echo host=${IP}
+ echo port=$PORT
+ echo user=$USER
+ echo pass=$(rclone obscure $PASS)
+ echo _connect=${IP}:${PORT}
+}
+
+. $(dirname "$0")/rclone-serve.bash
diff --git a/fstest/testserver/init.d/TestSFTPRcloneSSH b/fstest/testserver/init.d/TestSFTPRcloneSSH
new file mode 100755
index 0000000..989a5e6
--- /dev/null
+++ b/fstest/testserver/init.d/TestSFTPRcloneSSH
@@ -0,0 +1,67 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=rclone-serve-sftp-ssh
+IP=127.0.0.1
+PORT=28623
+PRIVATE_KEY=/tmp/${NAME}.key
+PUBLIC_KEY=/tmp/${NAME}.pub
+
+start() {
+
+cat >${PRIVATE_KEY} <<'#EOF'
+-----BEGIN OPENSSH PRIVATE KEY-----
+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
+vmZ2QrVK8TstC/AAAACm5jd0Bkb2dnZXI=
+-----END OPENSSH PRIVATE KEY-----
+#EOF
+chmod 600 ${PRIVATE_KEY}
+
+cat >${PUBLIC_KEY} <<'#EOF'
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC/t7p34BvEQu9GTuxmKmR626qATj+9Vt//o8y453U5I/I46wA6zjW29zhKThL7/pF4dbiXUMrLkgfSyY6wDWTnyQoWQ3U0KEdTnPQKK0CJnB2XvOq/E0mqUyZiIZO5uFB0ycOurQACeHKv1MWtxmZg9yn20dym31VBZ4thH/Xq1PndaemBO4xaWDIznxukKH9wM7uA6NLJceE8/GtTWp2vmD/lP0cWBB+LYJfy+TikNj5DDUuenUwykpPiL3FYYxTygMkbfTxGvNSL23PVFN4+1uzjFc7Lxxc5loA4MNCiTiJa8kSkaAlguOE24fNmJQNx9zVKRe8426LuPlEL+uxA5gHlJ3sBOUXgfKYDFkneI+755gFmLuXAg966D7jZaVWif4Pl05iauBw+kBm/vrsu2G/rYUya9zVIccY9ktLd1Z9tl5drK9737AiQoUS4M3vrNgBq+NJGb18EqfLon6egmClsMxU2dN80Dbx+w1uwJLo6wGMtuIWMwfGG5gAIxpU= user@rclone-serve-test
+#EOF
+chmod 600 ${PUBLIC_KEY}
+
+ run rclone serve sftp --authorized-keys "${PUBLIC_KEY}" --addr ${IP}:${PORT} ${DATADIR}
+
+ echo type=sftp
+ echo ssh=ssh -i ${PRIVATE_KEY} -o StrictHostKeyChecking=no -p ${PORT} user@${IP}
+ echo _connect=${IP}:${PORT}
+}
+
+. $(dirname "$0")/rclone-serve.bash
diff --git a/fstest/testserver/init.d/TestSMB b/fstest/testserver/init.d/TestSMB
new file mode 100755
index 0000000..4e10d59
--- /dev/null
+++ b/fstest/testserver/init.d/TestSMB
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=smb
+USER=rclone
+PASS=GNF3Cqeu
+WORKGROUP=thepub
+PORT=28630
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ docker run --rm -d --name $NAME \
+ -p 127.0.0.1:${PORT}:445 \
+ -p 127.0.0.1:${PORT}:445/udp \
+ dperson/samba \
+ -p \
+ -u "rclone;${PASS}" \
+ -w "${WORKGROUP}" \
+ -s "public;/share" \
+ -s "rclone;/rclone;yes;no;no;rclone"
+
+ echo type=smb
+ echo host=127.0.0.1
+ echo user=$USER
+ echo port=$PORT
+ echo pass=$(rclone obscure $PASS)
+ echo domain=$WORKGROUP
+ echo _connect=127.0.0.1:${PORT}
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestSMBKerberos b/fstest/testserver/init.d/TestSMBKerberos
new file mode 100755
index 0000000..f54eeb6
--- /dev/null
+++ b/fstest/testserver/init.d/TestSMBKerberos
@@ -0,0 +1,84 @@
+#!/usr/bin/env bash
+
+set -e
+
+IMAGE=rclone/test-smb-kerberos
+NAME=smb-kerberos
+USER=rclone
+DOMAIN=RCLONE
+REALM=RCLONE.LOCAL
+SMB_PORT=28633
+KRB5_PORT=28634
+
+# KRB5_CONFIG and KRB5CCNAME should be set by the caller but default
+# them here for the integration tests
+export TEMP_DIR=/tmp/rclone_krb5
+mkdir -p "${TEMP_DIR}"
+export KRB5_CONFIG=${KRB5_CONFIG:-${TEMP_DIR}/krb5.conf}
+export KRB5CCNAME=${KRB5CCNAME:-${TEMP_DIR}/ccache}
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ docker build -t ${IMAGE} --load - <<EOF
+FROM alpine:3.21
+RUN apk add --no-cache samba-dc
+RUN rm -rf /etc/samba/smb.conf /var/lib/samba \
+ && mkdir -p /var/lib/samba/private \
+ && samba-tool domain provision \
+ --use-rfc2307 \
+ --option acl_xattr:security_acl_name=user.NTACL \
+ --realm=$REALM \
+ --domain=$DOMAIN \
+ --server-role=dc \
+ --dns-backend=SAMBA_INTERNAL \
+ --host-name=localhost \
+ && samba-tool user add --random-password $USER \
+ && samba-tool user setexpiry $USER --noexpiry \
+ && mkdir -m 777 /share /rclone \
+ && cat <<EOS >> /etc/samba/smb.conf
+[global]
+server signing = auto
+[public]
+path = /share
+browseable = yes
+read only = yes
+guest ok = yes
+[rclone]
+path = /rclone
+browseable = yes
+read only = no
+guest ok = no
+valid users = rclone
+EOS
+CMD ["samba", "-i"]
+EOF
+
+ docker run --rm -d --name ${NAME} \
+ -p 127.0.0.1:${SMB_PORT}:445 \
+ -p 127.0.0.1:${SMB_PORT}:445/udp \
+ -p 127.0.0.1:${KRB5_PORT}:88 \
+ ${IMAGE}
+
+ # KRB5_CONFIG and KRB5CCNAME are set by the caller
+ cat > ${KRB5_CONFIG} <<EOF
+[libdefaults]
+ default_realm = ${REALM}
+[realms]
+${REALM} = {
+ kdc = localhost
+}
+EOF
+ docker cp ${KRB5_CONFIG} ${NAME}:/etc/krb5.conf
+ docker exec ${NAME} samba-tool user get-kerberos-ticket rclone --output-krb5-ccache=/tmp/ccache
+ docker cp ${NAME}:/tmp/ccache ${KRB5CCNAME}
+ sed -i -e "s/localhost/localhost:${KRB5_PORT}/" ${KRB5_CONFIG}
+
+ echo type=smb
+ echo host=localhost
+ echo port=$SMB_PORT
+ echo use_kerberos=true
+ echo _connect=127.0.0.1:${SMB_PORT}
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestSMBKerberosCcache b/fstest/testserver/init.d/TestSMBKerberosCcache
new file mode 100755
index 0000000..1238299
--- /dev/null
+++ b/fstest/testserver/init.d/TestSMBKerberosCcache
@@ -0,0 +1,85 @@
+#!/usr/bin/env bash
+
+set -e
+
+# Set default location for Kerberos config and ccache. Can be overridden by the caller
+# using environment variables RCLONE_TEST_CUSTOM_CCACHE_LOCATION and KRB5_CONFIG.
+export TEMP_DIR=/tmp/rclone_krb5_ccache
+mkdir -p "${TEMP_DIR}"
+export KRB5_CONFIG=${KRB5_CONFIG:-${TEMP_DIR}/krb5.conf}
+export RCLONE_TEST_CUSTOM_CCACHE_LOCATION=${RCLONE_TEST_CUSTOM_CCACHE_LOCATION:-${TEMP_DIR}/ccache}
+
+IMAGE=rclone/test-smb-kerberos-ccache
+NAME=smb-kerberos-ccache
+USER=rclone
+DOMAIN=RCLONE
+REALM=RCLONE.LOCAL
+SMB_PORT=28637
+KRB5_PORT=28638
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ docker build -t ${IMAGE} --load - <<EOF
+FROM alpine:3.21
+RUN apk add --no-cache samba-dc
+RUN rm -rf /etc/samba/smb.conf /var/lib/samba \
+ && mkdir -p /var/lib/samba/private \
+ && samba-tool domain provision \
+ --use-rfc2307 \
+ --option acl_xattr:security_acl_name=user.NTACL \
+ --realm=$REALM \
+ --domain=$DOMAIN \
+ --server-role=dc \
+ --dns-backend=SAMBA_INTERNAL \
+ --host-name=localhost \
+ && samba-tool user add --random-password $USER \
+ && samba-tool user setexpiry $USER --noexpiry \
+ && mkdir -m 777 /share /rclone \
+ && cat <<EOS >> /etc/samba/smb.conf
+[global]
+server signing = auto
+[public]
+path = /share
+browseable = yes
+read only = yes
+guest ok = yes
+[rclone]
+path = /rclone
+browseable = yes
+read only = no
+guest ok = no
+valid users = rclone
+EOS
+CMD ["samba", "-i"]
+EOF
+
+ docker run --rm -d --name ${NAME} \
+ -p 127.0.0.1:${SMB_PORT}:445 \
+ -p 127.0.0.1:${SMB_PORT}:445/udp \
+ -p 127.0.0.1:${KRB5_PORT}:88 \
+ ${IMAGE}
+
+ cat > "${KRB5_CONFIG}" <<EOF
+[libdefaults]
+ default_realm = ${REALM}
+[realms]
+${REALM} = {
+ kdc = localhost
+}
+EOF
+
+ docker cp "${KRB5_CONFIG}" ${NAME}:/etc/krb5.conf
+ docker exec ${NAME} samba-tool user get-kerberos-ticket rclone --output-krb5-ccache=/tmp/ccache
+ docker cp ${NAME}:/tmp/ccache "${RCLONE_TEST_CUSTOM_CCACHE_LOCATION}"
+ sed -i -e "s/localhost/localhost:${KRB5_PORT}/" "${KRB5_CONFIG}"
+
+ echo type=smb
+ echo host=localhost
+ echo port=$SMB_PORT
+ echo use_kerberos=true
+ echo kerberos_ccache=${RCLONE_TEST_CUSTOM_CCACHE_LOCATION}
+ echo _connect=127.0.0.1:${SMB_PORT}
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestSeafile b/fstest/testserver/init.d/TestSeafile
new file mode 100755
index 0000000..553e9d0
--- /dev/null
+++ b/fstest/testserver/init.d/TestSeafile
@@ -0,0 +1,72 @@
+#!/usr/bin/env bash
+
+set -e
+
+# environment variables passed on docker-compose
+export NAME=seafile7
+export MYSQL_ROOT_PASSWORD=pixenij4zacoguq0kopamid6
+export SEAFILE_ADMIN_EMAIL=seafile@rclone.org
+export SEAFILE_ADMIN_PASSWORD=pixenij4zacoguq0kopamid6
+export SEAFILE_IP=127.0.0.1
+export SEAFILE_PORT=8087
+export SEAFILE_TEST_DATA=${SEAFILE_TEST_DATA:-/tmp/seafile-test-data}
+export SEAFILE_VERSION=latest
+
+# make sure the data directory exists
+mkdir -p ${SEAFILE_TEST_DATA}/${NAME}
+
+# docker-compose project directory
+COMPOSE_DIR=$(dirname "$0")/seafile
+
+start() {
+ docker-compose --project-directory ${COMPOSE_DIR} --project-name ${NAME} --file ${COMPOSE_DIR}/docker-compose.yml up -d
+
+ # wait for Seafile server to start
+ seafile_endpoint="http://${SEAFILE_IP}:${SEAFILE_PORT}/"
+ wait_seconds=1
+ echo -n "Waiting for Seafile server to start"
+ for iterations in `seq 1 60`;
+ do
+ http_code=$(curl -s -o /dev/null -L -w '%{http_code}' "$seafile_endpoint" || true;)
+ if [ "$http_code" -eq 200 ]; then
+ echo
+ break
+ fi
+ echo -n "."
+ sleep $wait_seconds
+ done
+
+ # authentication token answer should be like: {"token":"dbf58423f1632b5b679a13b0929f1d0751d9250c"}
+ TOKEN=`curl --silent \
+ --data-urlencode username=${SEAFILE_ADMIN_EMAIL} -d password=${SEAFILE_ADMIN_PASSWORD} \
+ http://${SEAFILE_IP}:${SEAFILE_PORT}/api2/auth-token/ \
+ | sed 's/^{"token":"\(.*\)"}$/\1/'`
+
+ # create default library
+ curl --silent -o /dev/null -X POST -H "Authorization: Token ${TOKEN}" "http://${SEAFILE_IP}:${SEAFILE_PORT}/api2/default-repo/"
+
+ echo _connect=${SEAFILE_IP}:${SEAFILE_PORT}
+ echo type=seafile
+ echo url=http://${SEAFILE_IP}:${SEAFILE_PORT}/
+ echo user=${SEAFILE_ADMIN_EMAIL}
+ echo pass=$(rclone obscure ${SEAFILE_ADMIN_PASSWORD})
+ echo library=My Library
+}
+
+stop() {
+ if status ; then
+ docker-compose --project-directory ${COMPOSE_DIR} --project-name ${NAME} --file ${COMPOSE_DIR}/docker-compose.yml down
+ fi
+}
+
+status() {
+ if docker ps --format "{{.Names}}" | grep ^${NAME}_seafile_1$ >/dev/null ; then
+ echo "$NAME running"
+ else
+ echo "$NAME not running"
+ return 1
+ fi
+ return 0
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestSeafileEncrypted b/fstest/testserver/init.d/TestSeafileEncrypted
new file mode 100755
index 0000000..dd4b6bc
--- /dev/null
+++ b/fstest/testserver/init.d/TestSeafileEncrypted
@@ -0,0 +1,65 @@
+#!/usr/bin/env bash
+
+set -e
+
+# local variables
+TEST_LIBRARY=Encrypted
+TEST_LIBRARY_PASSWORD=SecretKey
+
+# environment variables passed on docker-compose
+export NAME=seafile7encrypted
+export MYSQL_ROOT_PASSWORD=pixenij4zacoguq0kopamid6
+export SEAFILE_ADMIN_EMAIL=seafile@rclone.org
+export SEAFILE_ADMIN_PASSWORD=pixenij4zacoguq0kopamid6
+export SEAFILE_IP=127.0.0.1
+export SEAFILE_PORT=8088
+export SEAFILE_TEST_DATA=${SEAFILE_TEST_DATA:-/tmp/seafile-test-data}
+export SEAFILE_VERSION=latest
+
+# make sure the data directory exists
+mkdir -p ${SEAFILE_TEST_DATA}/${NAME}
+
+# docker-compose project directory
+COMPOSE_DIR=$(dirname "$0")/seafile
+
+start() {
+ docker-compose --project-directory ${COMPOSE_DIR} --project-name ${NAME} --file ${COMPOSE_DIR}/docker-compose.yml up -d
+
+ # it takes some time for the database to be created
+ sleep 60
+
+ # authentication token answer should be like: {"token":"dbf58423f1632b5b679a13b0929f1d0751d9250c"}
+ TOKEN=`curl --silent \
+ --data-urlencode username=${SEAFILE_ADMIN_EMAIL} -d password=${SEAFILE_ADMIN_PASSWORD} \
+ http://${SEAFILE_IP}:${SEAFILE_PORT}/api2/auth-token/ \
+ | sed 's/^{"token":"\(.*\)"}$/\1/'`
+
+ # create encrypted library
+ curl --silent -o /dev/null -X POST -d "name=${TEST_LIBRARY}&passwd=${TEST_LIBRARY_PASSWORD}" -H "Authorization: Token ${TOKEN}" "http://${SEAFILE_IP}:${SEAFILE_PORT}/api2/repos/"
+
+ echo _connect=${SEAFILE_IP}:${SEAFILE_PORT}
+ echo type=seafile
+ echo url=http://${SEAFILE_IP}:${SEAFILE_PORT}/
+ echo user=${SEAFILE_ADMIN_EMAIL}
+ echo pass=$(rclone obscure ${SEAFILE_ADMIN_PASSWORD})
+ echo library=${TEST_LIBRARY}
+ echo library_key=$(rclone obscure ${TEST_LIBRARY_PASSWORD})
+}
+
+stop() {
+ if status ; then
+ docker-compose --project-directory ${COMPOSE_DIR} --project-name ${NAME} --file ${COMPOSE_DIR}/docker-compose.yml down
+ fi
+}
+
+status() {
+ if docker ps --format "{{.Names}}" | grep ^${NAME}_seafile_1$ >/dev/null ; then
+ echo "$NAME running"
+ else
+ echo "$NAME not running"
+ return 1
+ fi
+ return 0
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestSeafileV6 b/fstest/testserver/init.d/TestSeafileV6
new file mode 100755
index 0000000..e2365aa
--- /dev/null
+++ b/fstest/testserver/init.d/TestSeafileV6
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+
+set -e
+
+# local variables
+NAME=seafile6
+SEAFILE_IP=127.0.0.1
+SEAFILE_PORT=8086
+SEAFILE_ADMIN_EMAIL=seafile@rclone.org
+SEAFILE_ADMIN_PASSWORD=qebiwob7wafixif8sojiboj4
+SEAFILE_TEST_DATA=${SEAFILE_TEST_DATA:-/tmp/seafile-test-data}
+SEAFILE_VERSION=latest
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ # make sure the data directory exists
+ mkdir -p ${SEAFILE_TEST_DATA}/${NAME}
+
+ docker run --rm -d --name $NAME \
+ -e SEAFILE_SERVER_HOSTNAME=${SEAFILE_IP}:${SEAFILE_PORT} \
+ -e SEAFILE_ADMIN_EMAIL=${SEAFILE_ADMIN_EMAIL} \
+ -e SEAFILE_ADMIN_PASSWORD=${SEAFILE_ADMIN_PASSWORD} \
+ -v ${SEAFILE_TEST_DATA}/${NAME}:/shared \
+ -p ${SEAFILE_IP}:${SEAFILE_PORT}:80 \
+ seafileltd/seafile:${SEAFILE_VERSION}
+
+ # it takes some time for the database to be created
+ sleep 60
+
+ # authentication token answer should be like: {"token":"dbf58423f1632b5b679a13b0929f1d0751d9250c"}
+ TOKEN=`curl --silent \
+ --data-urlencode username=${SEAFILE_ADMIN_EMAIL} -d password=${SEAFILE_ADMIN_PASSWORD} \
+ http://${SEAFILE_IP}:${SEAFILE_PORT}/api2/auth-token/ \
+ | sed 's/^{"token":"\(.*\)"}$/\1/'`
+
+ # create default library
+ curl --silent -o /dev/null -X POST -H "Authorization: Token ${TOKEN}" "http://${SEAFILE_IP}:${SEAFILE_PORT}/api2/default-repo/"
+
+ echo _connect=${SEAFILE_IP}:${SEAFILE_PORT}
+ echo type=seafile
+ echo url=http://${SEAFILE_IP}:${SEAFILE_PORT}/
+ echo user=${SEAFILE_ADMIN_EMAIL}
+ echo pass=$(rclone obscure ${SEAFILE_ADMIN_PASSWORD})
+ echo library=My Library
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestSia b/fstest/testserver/init.d/TestSia
new file mode 100755
index 0000000..9b11caf
--- /dev/null
+++ b/fstest/testserver/init.d/TestSia
@@ -0,0 +1,55 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=Sia
+
+# shellcheck disable=SC1090
+. "$(dirname "$0")"/docker.bash
+
+# wait until Sia test network is up,
+# the Sia renter forms contracts on the blockchain
+# and the renter is upload ready
+wait_for_sia() {
+ until curl -A Sia-Agent -s "$1" | grep -q '"ready":true'
+ do
+ sleep 5
+ done
+}
+export -f wait_for_sia
+
+start() {
+ # use non-production sia port in test
+ SIA_CONN="127.0.0.1:39980"
+ # nebulouslabs/siaantfarm is stale, use up-to-date image
+ ANTFARM_IMAGE=ivandeex/sia-antfarm:latest
+
+ # pull latest antfarm image (dont use local image)
+ docker pull --quiet $ANTFARM_IMAGE
+
+ # start latest antfarm with default config
+ docker run --rm --detach --name "$NAME" \
+ --publish "${SIA_CONN}:9980" \
+ $ANTFARM_IMAGE
+
+ # wait until the test network is upload ready
+ timeout 300 bash -c "wait_for_sia ${SIA_CONN}/renter/uploadready"
+
+ # confirm backend type in the generated rclone.conf
+ echo "type=sia"
+ # override keys in the Sia section of generated rclone.conf
+ echo "api_url=http://${SIA_CONN}/"
+ # hint test harness where to probe for connection
+ echo "_connect=${SIA_CONN}"
+}
+
+stop() {
+ if status ; then
+ docker logs "$NAME" >> sia-test.log 2>&1
+ docker kill "$NAME"
+ echo "${NAME} stopped"
+ fi
+}
+
+# shellcheck disable=SC1090
+. "$(dirname "$0")"/run.bash
diff --git a/fstest/testserver/init.d/TestSwiftAIO b/fstest/testserver/init.d/TestSwiftAIO
new file mode 100755
index 0000000..7e20bd6
--- /dev/null
+++ b/fstest/testserver/init.d/TestSwiftAIO
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=swift-aio
+PORT=28628
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ # We need to replace the remakerings in the container to create Policy-1.
+ docker run --rm -d --name ${NAME} \
+ -p 127.0.0.1:${PORT}:8080 \
+ -v $(dirname "$0")/TestSwiftAIO.d/remakerings:/etc/swift/remakerings:ro \
+ openstackswift/saio
+
+ echo type=swift
+ echo env_auth=false
+ echo user=test:tester
+ echo key=testing
+ echo auth=http://127.0.0.1:${PORT}/auth/v1.0
+ echo _connect=127.0.0.1:${PORT}
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestSwiftAIO.d/remakerings b/fstest/testserver/init.d/TestSwiftAIO.d/remakerings
new file mode 100755
index 0000000..27c49b1
--- /dev/null
+++ b/fstest/testserver/init.d/TestSwiftAIO.d/remakerings
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+if ! grep -q "^\[storage-policy:1\]" swift.conf; then
+ cat <<EOF >> swift.conf
+
+[storage-policy:1]
+name = Policy-1
+EOF
+fi
+
+rm -f *.builder *.ring.gz backups/*.builder backups/*.ring.gz
+
+swift-ring-builder object.builder create 10 1 1
+swift-ring-builder object.builder add r1z1-127.0.0.1:6200/swift-d0 1
+swift-ring-builder object.builder add r1z1-127.0.0.1:6200/swift-d1 1
+swift-ring-builder object.builder add r1z1-127.0.0.1:6200/swift-d2 1
+swift-ring-builder object.builder add r1z1-127.0.0.1:6200/swift-d3 1
+swift-ring-builder object.builder add r1z1-127.0.0.1:6200/swift-d4 1
+swift-ring-builder object.builder add r1z1-127.0.0.1:6200/swift-d5 1
+swift-ring-builder object.builder rebalance
+swift-ring-builder container.builder create 10 1 1
+swift-ring-builder container.builder add r1z1-127.0.0.1:6201/swift-d0 1
+swift-ring-builder container.builder add r1z1-127.0.0.1:6201/swift-d1 1
+swift-ring-builder container.builder add r1z1-127.0.0.1:6201/swift-d2 1
+swift-ring-builder container.builder add r1z1-127.0.0.1:6201/swift-d3 1
+swift-ring-builder container.builder add r1z1-127.0.0.1:6201/swift-d4 1
+swift-ring-builder container.builder add r1z1-127.0.0.1:6201/swift-d5 1
+swift-ring-builder container.builder rebalance
+swift-ring-builder account.builder create 10 1 1
+swift-ring-builder account.builder add r1z1-127.0.0.1:6202/swift-d0 1
+swift-ring-builder account.builder add r1z1-127.0.0.1:6202/swift-d1 1
+swift-ring-builder account.builder add r1z1-127.0.0.1:6202/swift-d2 1
+swift-ring-builder account.builder add r1z1-127.0.0.1:6202/swift-d3 1
+swift-ring-builder account.builder add r1z1-127.0.0.1:6202/swift-d4 1
+swift-ring-builder account.builder add r1z1-127.0.0.1:6202/swift-d5 1
+swift-ring-builder account.builder rebalance
+
+# For Policy-1:
+swift-ring-builder object-1.builder create 10 1 1
+swift-ring-builder object-1.builder add r1z1-127.0.0.1:6200/swift-d0 1
+swift-ring-builder object-1.builder add r1z1-127.0.0.1:6200/swift-d1 1
+swift-ring-builder object-1.builder add r1z1-127.0.0.1:6200/swift-d2 1
+swift-ring-builder object-1.builder add r1z1-127.0.0.1:6200/swift-d3 1
+swift-ring-builder object-1.builder add r1z1-127.0.0.1:6200/swift-d4 1
+swift-ring-builder object-1.builder add r1z1-127.0.0.1:6200/swift-d5 1
+swift-ring-builder object-1.builder rebalance
diff --git a/fstest/testserver/init.d/TestSwiftAIOsegments b/fstest/testserver/init.d/TestSwiftAIOsegments
new file mode 100755
index 0000000..db02630
--- /dev/null
+++ b/fstest/testserver/init.d/TestSwiftAIOsegments
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+set -e
+
+NAME=swift-aio-segments
+PORT=28632
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ # We need to replace the remakerings in the container to create Policy-1.
+ docker run --rm -d --name ${NAME} \
+ -p 127.0.0.1:${PORT}:8080 \
+ -v $(dirname "$0")/TestSwiftAIO.d/remakerings:/etc/swift/remakerings:ro \
+ openstackswift/saio
+
+ echo type=swift
+ echo env_auth=false
+ echo user=test:tester
+ echo key=testing
+ echo auth=http://127.0.0.1:${PORT}/auth/v1.0
+ echo use_segments_container=false
+ echo _connect=127.0.0.1:${PORT}
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestWebdavInfiniteScale b/fstest/testserver/init.d/TestWebdavInfiniteScale
new file mode 100755
index 0000000..f13e22b
--- /dev/null
+++ b/fstest/testserver/init.d/TestWebdavInfiniteScale
@@ -0,0 +1,46 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=infinitescale
+USER=admin
+PASS=admin
+PORT=28639
+CONF_DIR=/tmp/ocis-config
+mkdir -p ${CONF_DIR}
+chmod 777 ${CONF_DIR} || true
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ docker run --rm --name $NAME \
+ -v ${CONF_DIR}:/etc/ocis \
+ -e "OCIS_INSECURE=true" \
+ -e "IDM_ADMIN_PASSWORD=$PASS" \
+ -e "OCIS_FORCE_CONFIG_OVERWRITE=true" \
+ -e "OCIS_URL=https://127.0.0.1:$PORT" \
+ owncloud/ocis \
+ init
+
+ docker run --rm -d --name $NAME \
+ -e "OCIS_LOG_LEVEL=debug" \
+ -e "OCIS_LOG_PRETTY=true" \
+ -e "OCIS_URL=https://127.0.0.1:$PORT" \
+ -e "OCIS_ADMIN_USER_ID=some-admin-user-id-0000-100000000000" \
+ -e "IDM_ADMIN_PASSWORD=$PASS" \
+ -e "OCIS_INSECURE=true" \
+ -e "PROXY_ENABLE_BASIC_AUTH=true" \
+ -v ${CONF_DIR}:/etc/ocis \
+ -p 127.0.0.1:${PORT}:9200 \
+ owncloud/ocis
+
+ echo type=webdav
+ echo url=https://127.0.0.1:${PORT}/dav/spaces/some-admin-user-id-0000-100000000000
+ echo user=$USER
+ echo pass=$(rclone obscure $PASS)
+ echo vendor=infinitescale
+ echo _connect=127.0.0.1:${PORT}
+ echo _connect_delay=5s
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestWebdavNextcloud b/fstest/testserver/init.d/TestWebdavNextcloud
new file mode 100755
index 0000000..42766e5
--- /dev/null
+++ b/fstest/testserver/init.d/TestWebdavNextcloud
@@ -0,0 +1,29 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=nextcloud
+USER=rclone
+PASS=ArmorAbleMale6
+PORT=28629
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ docker run --rm -d --name $NAME \
+ -e "SQLITE_DATABASE=nextcloud.db" \
+ -e "NEXTCLOUD_ADMIN_USER=rclone" \
+ -e "NEXTCLOUD_ADMIN_PASSWORD=$PASS" \
+ -e "NEXTCLOUD_TRUSTED_DOMAINS=*.*.*.*" \
+ -p 127.0.0.1:${PORT}:80 \
+ nextcloud:latest
+
+ echo type=webdav
+ echo url=http://127.0.0.1:${PORT}/remote.php/dav/files/$USER/
+ echo user=$USER
+ echo pass=$(rclone obscure $PASS)
+ echo vendor=nextcloud
+ echo _connect=127.0.0.1:${PORT}
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestWebdavOwncloud b/fstest/testserver/init.d/TestWebdavOwncloud
new file mode 100755
index 0000000..d2dc238
--- /dev/null
+++ b/fstest/testserver/init.d/TestWebdavOwncloud
@@ -0,0 +1,33 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=owncloud
+USER=rclone
+PASS=HarperGrayerFewest5
+PORT=38081
+
+. $(dirname "$0")/docker.bash
+
+start() {
+ docker run --rm -d --name $NAME \
+ -e "OWNCLOUD_DOMAIN=localhost:8080" \
+ -e "OWNCLOUD_DB_TYPE=sqlite" \
+ -e "OWNCLOUD_DB_NAME=owncloud.db" \
+ -e "OWNCLOUD_ADMIN_USERNAME=$USER" \
+ -e "OWNCLOUD_ADMIN_PASSWORD=$PASS" \
+ -e "OWNCLOUD_MYSQL_UTF8MB4=true" \
+ -e "OWNCLOUD_REDIS_ENABLED=false" \
+ -e "OWNCLOUD_TRUSTED_DOMAINS=127.0.0.1" \
+ -p 127.0.0.1:${PORT}:8080 \
+ owncloud/server
+
+ echo type=webdav
+ echo url=http://127.0.0.1:${PORT}/remote.php/webdav/
+ echo user=$USER
+ echo pass=$(rclone obscure $PASS)
+ echo vendor=owncloud
+ echo _connect=127.0.0.1:${PORT}
+}
+
+. $(dirname "$0")/run.bash
diff --git a/fstest/testserver/init.d/TestWebdavRclone b/fstest/testserver/init.d/TestWebdavRclone
new file mode 100755
index 0000000..e740ecc
--- /dev/null
+++ b/fstest/testserver/init.d/TestWebdavRclone
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+set -e
+
+NAME=rclone-serve-webdav
+USER=rclone
+PASS=PagansSwimExpiry9
+IP=127.0.0.1
+PORT=28620
+
+start() {
+ run rclone serve webdav --user $USER --pass $PASS --addr ${IP}:${PORT} ${DATADIR}
+
+ echo type=webdav
+ echo vendor=rclone
+ echo url=http://${IP}:${PORT}/
+ echo user=$USER
+ echo pass=$(rclone obscure $PASS)
+ echo _connect=${IP}:$PORT
+}
+
+. $(dirname "$0")/rclone-serve.bash
diff --git a/fstest/testserver/init.d/docker.bash b/fstest/testserver/init.d/docker.bash
new file mode 100644
index 0000000..1bcc2c2
--- /dev/null
+++ b/fstest/testserver/init.d/docker.bash
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+stop() {
+ if status ; then
+ docker stop "$NAME"
+ echo "$NAME stopped"
+ fi
+}
+
+status() {
+ if docker ps --format '{{.Names}}' | grep -q "^${NAME}$" ; then
+ echo "$NAME running"
+ else
+ echo "$NAME not running"
+ return 1
+ fi
+ return 0
+}
+
+docker_ip() {
+ docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{"\n"}}{{end}}' "$NAME" | head -n 1
+}
diff --git a/fstest/testserver/init.d/rclone-serve.bash b/fstest/testserver/init.d/rclone-serve.bash
new file mode 100644
index 0000000..408960e
--- /dev/null
+++ b/fstest/testserver/init.d/rclone-serve.bash
@@ -0,0 +1,42 @@
+#!/usr/bin/env bash
+
+# start an "rclone serve" server
+
+PIDFILE=/tmp/${NAME}.pid
+DATADIR=/tmp/${NAME}-data
+
+stop() {
+ if status ; then
+ pid=$(cat "$PIDFILE")
+ kill "$pid"
+ rm "$PIDFILE"
+ echo "$NAME stopped"
+ fi
+}
+
+status() {
+ if [ -e "$PIDFILE" ]; then
+ pid=$(cat "$PIDFILE")
+ if kill -0 "$pid" >/dev/null 2>&1; then
+ # echo "$NAME running"
+ return 0
+ else
+ rm "$PIDFILE"
+ fi
+ fi
+ # echo "$NAME not running"
+ return 1
+}
+
+run() {
+ if ! status ; then
+ mkdir -p "$DATADIR"
+ nohup "$@" >> "/tmp/${NAME}.log" 2>&1 </dev/null &
+ pid=$!
+ echo $pid > "$PIDFILE"
+ disown "$pid"
+ fi
+}
+
+# shellcheck disable=SC1090
+. "$(dirname "$0")/run.bash"
diff --git a/fstest/testserver/init.d/run.bash b/fstest/testserver/init.d/run.bash
new file mode 100644
index 0000000..ff1f70e
--- /dev/null
+++ b/fstest/testserver/init.d/run.bash
@@ -0,0 +1,101 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+RUN_BASE="${STATE_DIR:-${XDG_RUNTIME_DIR:-/tmp}/rclone-test-server}"
+: "${NAME:=$(basename "$0")}"
+RUN_ROOT="${RUN_BASE}/${NAME}"
+RUN_STATE="${RUN_ROOT}/state"
+RUN_LOCK_FILE="${RUN_ROOT}/lock"
+RUN_REF_COUNT="${RUN_STATE}/refcount"
+RUN_OUTPUT="${RUN_STATE}/env"
+
+mkdir -p "${RUN_STATE}"
+[[ -f "${RUN_REF_COUNT}" ]] || echo 0 >"${RUN_REF_COUNT}"
+[[ -f "${RUN_OUTPUT}" ]] || : >"${RUN_OUTPUT}"
+: > "${RUN_LOCK_FILE}" # ensure file exists
+
+# status helper that won't trip set -e
+_is_running() { set +e; status >/dev/null 2>&1; local rc=$?; set -e; return $rc; }
+
+_acquire_lock() {
+ # open fd 9 on lock file and take exclusive lock
+ exec 9>"${RUN_LOCK_FILE}"
+ flock -x 9
+}
+
+_release_lock() {
+ flock -u 9
+ exec 9>&-
+}
+
+case "${1:-}" in
+ start)
+ _acquire_lock
+ trap '_release_lock' EXIT
+
+ rc=$(cat "${RUN_REF_COUNT}" 2>/dev/null || echo 0)
+
+ if (( rc == 0 )); then
+ # First client: ensure a clean instance, then start and cache env
+ if _is_running; then
+ stop || true
+ fi
+ if ! out="$(start)"; then
+ echo "failed to start" >&2
+ exit 1
+ fi
+ printf "%s\n" "$out" > "${RUN_OUTPUT}"
+ else
+ # Already owned: make sure it’s still up; if not, restart and refresh env
+ if ! _is_running; then
+ if ! out="$(start)"; then
+ echo "failed to restart" >&2
+ exit 1
+ fi
+ printf "%s\n" "$out" > "${RUN_OUTPUT}"
+ fi
+ fi
+
+ rc=$((rc+1)); echo "${rc}" > "${RUN_REF_COUNT}"
+ cat "${RUN_OUTPUT}"
+
+ trap - EXIT
+ _release_lock
+ ;;
+
+ stop)
+ _acquire_lock
+ trap '_release_lock' EXIT
+
+ rc=$(cat "${RUN_REF_COUNT}" 2>/dev/null || echo 0)
+ if (( rc > 0 )); then rc=$((rc-1)); fi
+ echo "${rc}" > "${RUN_REF_COUNT}"
+ if (( rc == 0 )) && _is_running; then
+ stop || true
+ fi
+
+ trap - EXIT
+ _release_lock
+ ;;
+
+ reset)
+ _acquire_lock
+ trap '_release_lock' EXIT
+
+ stop || true
+ rm -rf "${RUN_BASE}"
+
+ trap - EXIT
+ _release_lock
+ ;;
+
+ status)
+ # passthrough; do NOT take the lock
+ status
+ ;;
+
+ *)
+ echo "usage: $0 {start|stop|reset|status}" >&2
+ exit 2
+ ;;
+esac
diff --git a/fstest/testserver/init.d/seafile/docker-compose.yml b/fstest/testserver/init.d/seafile/docker-compose.yml
new file mode 100644
index 0000000..8e0a099
--- /dev/null
+++ b/fstest/testserver/init.d/seafile/docker-compose.yml
@@ -0,0 +1,31 @@
+version: '2.0'
+services:
+ db:
+ image: mariadb:10.5
+ environment:
+ - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
+ - MYSQL_LOG_CONSOLE=true
+ volumes:
+ - ${SEAFILE_TEST_DATA}/${NAME}/seafile-mysql/db:/var/lib/mysql
+
+ memcached:
+ image: memcached:1.6.9
+ entrypoint: memcached -m 256
+
+ seafile:
+ image: seafileltd/seafile-mc:${SEAFILE_VERSION}
+ ports:
+ - "${SEAFILE_IP}:${SEAFILE_PORT}:80"
+ volumes:
+ - ${SEAFILE_TEST_DATA}/${NAME}/seafile-data:/shared
+ environment:
+ - DB_HOST=db
+ - DB_ROOT_PASSWD=${MYSQL_ROOT_PASSWORD}
+ - TIME_ZONE=Etc/UTC
+ - SEAFILE_ADMIN_EMAIL=${SEAFILE_ADMIN_EMAIL}
+ - SEAFILE_ADMIN_PASSWORD=${SEAFILE_ADMIN_PASSWORD}
+ - SEAFILE_SERVER_LETSENCRYPT=false
+ - SEAFILE_SERVER_HOSTNAME=${SEAFILE_IP}:${SEAFILE_PORT}
+ depends_on:
+ - db
+ - memcached