index.html :: rss :: github :: telegram :: email

rsync: force permissions and owner on destenation

20 Apr 2025

tl;dr: rsync --usermap=dst:src --groupmap=src:dst --chmod=F664 $from $to

preface

for a years i have the media alias in my shell, that just scp given file to a www-root, and copy a direct link to the clipboard. i might simply run "media config.yaml" from anywhere in filesystem, and in just a second thers is a direct, non-expiring link to a file, right in my system clipboard.

it was implemented in the most straitforward way: scp to copy files to a remote, and urlencode (from url-tools zsh plugin) to make a properly escaped url from a filesystem path.

problem

until recently it works just fine, but now the syncthings daemon watches the same directory. it appears that i have to unify the permissions, to make files accessible by the web-server, and read-writable by syncthing.

the naive solution would be to add something like this to a script:

# ...
scp $src web-server:/var/www/media/
ssh web-server "chmod 0664 /var/www/media/$src; \
    chown www-data:www-data /var/www/media/$src"
...

but that feels like an ugly solution, and i don't want to open one more ssh connection, just because of time it takes.

solution

it appeared that rsync fits perfectly for this case. by defualt, rsync will keep the source's files owner and permissions, even if no such user or group exists on the remote. but we have an options to instruct rsync to remap, or even override owner and permissions on a remote.

we going to use three flags:

--usermap='*:www-data', which reads as "any source's owner becomes www-data on a remote machine",

--groupmap='*:www-data which is the same as above, but for groups, and

--chmod=F664, which tells rsync to apply 0664 permissions to destenation files.

the script

the updated version of the media script below:

if [[ "$1" == "" ]]; then
  echo "Usage: media file/to/upload"
  return 1
fi

file=$(echo $1 | awk -F "/" '{print $NF}')

rsync --progress --chmod=F664 \
    --usermap='*:www-data' \
    --groupmap='*:www-data' \
    "$1" web-server:/var/www/media/

result="https://media.nikonov.tech/$(urlencode $file)"
echo "Done! Link copied to the clipboard\n$result"
echo -n $result | pbcopy

refer to man 1 rsync for details