This is an attempt at creating an indelible dropbox for data over rsync. One might, for example, want to push data to a server and have reasonably high confidence that the client is powerless to delete it. I suggest running this atop a file system which does data unification, such as HAMMER or ZFS, but this is not critical by any means. This is probably not the right thing to be using if your dropbox grows to be dozens of gigabytes big, but for smaller things, it should work fine.
Overview¶
We create three sets of files: staging
, staged
, and backup
.
Clients write into the staging
area and a post-xfer hook copies (with
rsync, of course!) data into the staged
area, being sure to make a
time-stamped backup in the backup
area. Since the client does not have
(write) access to the staged
or backup
areas, we can be reasonably
sure that the client cannot actually delete it.
Directories¶
At some path of your choosing, PATH, make a directory tree like this:
drwxr-xr-x 2 root wheel 2 Sep 22 07:24 backups
drwxr-xr-x 2 root wheel 2 Sep 22 07:24 staged
drwxr-xr-x 2 dropbox dropbox 2 Sep 22 07:24 staging
rsyncd.conf¶
First, tell rsyncd about the staging area. Since we’re being a little
paranoid, we’ll have it chroot into the staging area, as well as adopt the
user and group IDs of some under-privileged user. For some applications, we
might leave out the delete part of the refuse options
directive, allowing
the client to appear to have deleted files.
[staging]
path = /PATH/staging
read only = false
timeout = 30
max connections = 1
post-xfer exec = /root/post-staging
use chroot = true
uid = dropbox
gid = dropbox
refuse options = delete times
/root/post-staging¶
And here’s the magic sauce which runs (as root
, not as dropbox
)
after each client connection:
#!shell
#!/bin/sh
BACKUPS=${RSYNC_MODULE_PATH}/../backups
LOGS=${RSYNC_MODULE_PATH}/../logs
STAGED=${RSYNC_MODULE_PATH}/../staged
UNIQNAME=`date +%s`:`ls ${LOGS} | wc -l | sed -e "s/ //g"`
# Stage the files, being sure to make backups
/usr/local/bin/rsync -avc --inplace \
--backup --backup-dir ${BACKUPS}/${UNIQNAME} \
--log-file ${LOGS}/${UNIQNAME}.rsy \
--out-format "XFER %n" \
${RSYNC_MODULE_PATH}/. ${STAGED}/. \
> ${LOGS}/${UNIQNAME}.fns
# Build a manifest of hashes of staged contents
cat ${LOGS}/${UNIQNAME}.fns | \
sed -ne '/[^\/]$/ s/^.*XFER //p' | \
perl -ne 'my $r; chomp; while (/(.*)\\#(\d\d\d)(.*)/) {
$r .= $1 . chr (oct $2); $_ = $3 }; $r .= $_; print $r; print "\0"' | \
(cd ${STAGED}; xargs -0 sha256 -r) > ${LOGS}/${UNIQNAME}.sha