使用sshfs fuse mount的多用户目录的rsync失败

时间:2017-04-17 18:34:16

标签: linux rsync fuse sshfs

我使用rsync在多个人使用的linux服务器中自动定期同步主文件夹(root用户)。用户需要的服务是通过sshfs挂载远程目录的可能性。但是,当存在sshfs挂载时,rsync将无法提供以下消息

rsync: readlink_stat("/home/???/???") failed: Permission denied (13)
IO error encountered -- skipping file deletion
...
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1183) [sender=3.1.1]

由于此错误,自动同步无法按预期工作,特别是由于跳过文件删除和非零退出代码。只有安装了home的文件系统才需要同步,因此需要的行为是忽略sshfs挂载。 -x / --one-file-system rsync选项无法解析它。

https://www.agwa.name/blog/post/how_fuse_can_break_rsync_backups清楚地解释了这个问题。后续文章(https://www.agwa.name/blog/post/easily_running_fuse_in_an_isolated_mount_namespace)提出了一个解决方案,虽然不是可接受的解决方案,因为保险丝座只对创建挂载的过程可见。

我正在寻找一种不影响sshfs可用性的解决方案,对用户来说是透明的。

2 个答案:

答案 0 :(得分:1)

如果在rsync命令中排除了保险丝安装点,则不会出现此错误。由于它是自动同步,因此可以使用mount命令获取所有保险丝安装点。 mount命令的输出可能因系统而异,但在debian中,jessie sshfs挂载在/ path / to / mount / point类型fuse.sshfs(rw,...)上显示为USER @ HOST:MOUNTED_DIR。在bash + sed中自动排除保险丝座的一种简单方法如下

SOURCE="/home/"

FUSEEXCLUDE=( $( mount |
  sed -rn "
    / type fuse/ {
      s|^[^ ]+ on ([^ ]+) type fuse.+|\1|;
      /^${SOURCE//\//\\\/}.+/ {
        s|^${SOURCE//\//\\\/}| --exclude |;
        p;
      }
    }" ) )

rsync $OPTIONS "${FUSEEXCLUDE[@]}" "$SOURCE" "$TARGET"

答案 1 :(得分:0)

问题是FUSE拒绝其他用户(包括root用户)的统计信息访问。 Rsync要求对指定的所有源文件和目录具有统计信息访问权限。但是,当另一个用户拥有的rsync进程统计FUSE挂载点时,FUSE拒绝该进程对挂载点属性的访问,从而导致rsync抛出“拒绝权限”错误。 Mauricio Villega的解决方案通过告诉rsync跳过mount命令列出的FUSE安装点来工作。这是Villega解决方案的另一个版本,该版本使用findmnt命令指定文件系统类型的白名单。我选择了ext3和ext4,但是您可以根据需要添加其他类型。

#!/bin/sh
# Which paths to rsync (note the lack of trailing slash tells rsync to preserve source path name at destination).
SOURCES=(
/home
)
# Which filesystem types are supported.
FSTYPES=(
ext3
ext4
)
# Rsync each source.
for SOURCE in ${SOURCES[@]}; do
  # Build exclusion list (array of "--exclude=PATH").
  excludedPaths=$(findmnt --invert --list --noheadings --output TARGET --types $(IFS=',';echo "${FSTYPES[*]}"))
  printf -v exclusionList -- "--exclude=%s " ${excludedPaths[@]}
  # Rsync.
  rsync --archive ${exclusionList[@]} --hard-links --delete --inplace --one-file-system ${SOURCE} /backup
done

请注意,它在循环内部构建了排除列表,以解决此解决方案的根本问题。该问题归因于实时系统中的rsync,用户可以在其中运行rsync的同时创建新的FUSE挂载点。排除列表需要经常更新,以包括新的FUSE挂载点。您可以通过修改SOURCES数组,按每个用户名进一步划分主目录。

SOURCES=(
/home/user1
/home/user2
)

如果使用的是LVM,另一种解决方案是从LVM快照进行rsync。 LVM快照提供了与其链接的逻辑卷的简单视图(例如,没有FUSE挂载点)和冻结视图。缺点是您必须为LVM快照的写时复制(COW)活动保留空间。完成LVM快照后,丢弃它至关重要。否则,随着修改,LVM快照的大小将继续增长。这是使用LVM快照的示例脚本。请注意,它不需要为rsync构建排除列表。

# Create and mount LVM snapshot.
lvcreate --extents 100%FREE --snapshot --name snapRoot /dev/vgSystem/lvRoot
mount -o ro /dev/mapper/snapRoot /root/mnt # Note that only root has access to this mount-point.
# Rsync each source.
for SOURCE in ${SOURCES[@]}; do
  rsync --archive --hard-links --delete --inplace --one-file-system /root/mnt/${SOURCE} /backup
done
# Discard LVM snapshot.
umount /root/mnt
lvremove vgSystem/snapRoot

参考文献:

"How FUSE Can Break Rsync Backups"