更好的 `chowning` 未知 uid/gid 文件 `rsync`(包括 --exclude-from 文件)将被视为源文件

Better way of `chowning` unknown uid/gid of files `rsync` (including an --exclude-from file) would consider as source files

我正在尝试更改我的 rsync 命令在执行 rsync 命令之前将其视为源文件的文件的未知 uid 和 gid。

我的 rsync 命令包含一个排除文件。

我需要这样做的原因在我的问题 中有解释。

我试过这个find命令:

find /cygdrive/f -uid 4294967295 -exec chown 544. '{}' + -o -gid 4294967295 -exec chown .197121 '{}' +

但是,它不处理排除文件。我的意思是,上面的 find 在整个 f 驱动器中搜索匹配未知 uid/gid 的文件,然后 chowns 它们。我的 rsync 查看驱动器 f 并复制除了排除文件中的文件之外的所有文件。我不想 chown 任何 rsync 不复制的 Win7 副文件。

例如,Win7 保护其某些 hidden/sys 文件的方法之一是将它们的 uid 和 gid 设置为 4294967295(例如 c:\pagefil.sys 和 c:\hiberfil.sys).我已经在 rsync 排除文件中排除了这两个文件,我想单独保留它们的 Win7 端 uid/gid。 find 命令会 chown 他们。

我还尝试解析一个 ls 列表,这可能有效,但速度非常慢。因为我只处理 Win7 文件,所以我认为 ls 适合解析。

chowning 脚本之前使用 ls 列表(或解析 find 输出)之前,是否有更好的方法来解决我的问题?

另一种更精确的方法,但对我来说速度慢且需要更困难的脚本,是解析 rsync --dry-run ... 列表以确定哪些项目需要 chowning

编辑 2015-12-13:不幸的是,rsync --dry-run ... 不会生成关于在干 运行 期间无法设置 UID/GID 的警告,因此该方法已失效。

但是,我找到了 rsync 的源代码,在我看来,修改它非常容易,这样 UID/GID 就可以设置为 UID,并且如果在会话期间发现错误的 UID/GID,则执行 rsync 命令的进程 运行 的 GID。

谁能给我总结一下在Win7电脑上编译rsync源代码需要什么工具?

这里是来自源代码的rsync.c(搜索'impossible to set'):

    int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
           const char *fnamecmp, int flags)
{
    int updated = 0;
    stat_x sx2;
    int change_uid, change_gid;
    mode_t new_mode = file->mode;
    int inherit;

    if (!sxp) {
        if (dry_run)
            return 1;
        if (link_stat(fname, &sx2.st, 0) < 0) {
            rsyserr(FERROR_XFER, errno, "stat %s failed",
                full_fname(fname));
            return 0;
        }
        init_stat_x(&sx2);
        sxp = &sx2;
        inherit = !preserve_perms;
    } else
        inherit = !preserve_perms && file->flags & FLAG_DIR_CREATED;

    if (inherit && S_ISDIR(new_mode) && sxp->st.st_mode & S_ISGID) {
        /* We just created this directory and its setgid
         * bit is on, so make sure it stays on. */
        new_mode |= S_ISGID;
    }

    if (daemon_chmod_modes && !S_ISLNK(new_mode))
        new_mode = tweak_mode(new_mode, daemon_chmod_modes);

#ifdef SUPPORT_ACLS
    if (preserve_acls && !S_ISLNK(file->mode) && !ACL_READY(*sxp))
        get_acl(fname, sxp);
#endif

    change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file);
    change_gid = gid_ndx && !(file->flags & FLAG_SKIP_GROUP)
          && sxp->st.st_gid != (gid_t)F_GROUP(file);
#ifndef CAN_CHOWN_SYMLINK
    if (S_ISLNK(sxp->st.st_mode)) {
        ;
    } else
#endif
    if (change_uid || change_gid) {
        if (DEBUG_GTE(OWN, 1)) {
            if (change_uid) {
                rprintf(FINFO,
                    "set uid of %s from %u to %u\n",
                    fname, (unsigned)sxp->st.st_uid, F_OWNER(file));
            }
            if (change_gid) {
                rprintf(FINFO,
                    "set gid of %s from %u to %u\n",
                    fname, (unsigned)sxp->st.st_gid, F_GROUP(file));
            }
        }
        if (am_root >= 0) {
            uid_t uid = change_uid ? (uid_t)F_OWNER(file) : sxp->st.st_uid;
            gid_t gid = change_gid ? (gid_t)F_GROUP(file) : sxp->st.st_gid;
            if (do_lchown(fname, uid, gid) != 0) {
                /* We shouldn't have attempted to change uid
                 * or gid unless have the privilege. */
                rsyserr(FERROR_XFER, errno, "%s %s failed",
                    change_uid ? "chown" : "chgrp",
                    full_fname(fname));
                goto cleanup;
            }
            if (uid == (uid_t)-1 && sxp->st.st_uid != (uid_t)-1)
                rprintf(FERROR_XFER, "uid 4294967295 (-1) is impossible to set on %s\n", full_fname(fname));
            if (gid == (gid_t)-1 && sxp->st.st_gid != (gid_t)-1)
                rprintf(FERROR_XFER, "gid 4294967295 (-1) is impossible to set on %s\n", full_fname(fname));
            /* A lchown had been done, so we need to re-stat if
             * the destination had the setuid or setgid bits set
             * (due to the side effect of the chown call). */
            if (sxp->st.st_mode & (S_ISUID | S_ISGID)) {
                link_stat(fname, &sxp->st,
                      keep_dirlinks && S_ISDIR(sxp->st.st_mode));
            }
        }
        updated = 1;
    }

#ifdef SUPPORT_XATTRS
    if (am_root < 0)
        set_stat_xattr(fname, file, new_mode);
    if (preserve_xattrs && fnamecmp)
        set_xattr(fname, file, fnamecmp, sxp);
#endif

    if (!preserve_times
     || (!(preserve_times & PRESERVE_DIR_TIMES) && S_ISDIR(sxp->st.st_mode))
     || (!(preserve_times & PRESERVE_LINK_TIMES) && S_ISLNK(sxp->st.st_mode)))
        flags |= ATTRS_SKIP_MTIME;
    if (!(flags & ATTRS_SKIP_MTIME)
        && cmp_time(sxp->st.st_mtime, file->modtime) != 0) {
        int ret = set_modtime(fname, file->modtime, F_MOD_NSEC(file), sxp->st.st_mode);
        if (ret < 0) {
            rsyserr(FERROR_XFER, errno, "failed to set times on %s",
                full_fname(fname));
            goto cleanup;
        }
        if (ret == 0) /* ret == 1 if symlink could not be set */
            updated = 1;
        else
            file->flags |= FLAG_TIME_FAILED;
    }

#ifdef SUPPORT_ACLS
    /* It's OK to call set_acl() now, even for a dir, as the generator
     * will enable owner-writability using chmod, if necessary.
     * 
     * If set_acl() changes permission bits in the process of setting
     * an access ACL, it changes sxp->st.st_mode so we know whether we
     * need to chmod(). */
    if (preserve_acls && !S_ISLNK(new_mode)) {
        if (set_acl(fname, file, sxp, new_mode) > 0)
            updated = 1;
    }
#endif

#ifdef HAVE_CHMOD
    if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) {
        int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
        if (ret < 0) {
            rsyserr(FERROR_XFER, errno,
                "failed to set permissions on %s",
                full_fname(fname));
            goto cleanup;
        }
        if (ret == 0) /* ret == 1 if symlink could not be set */
            updated = 1;
    }
#endif

    if (INFO_GTE(NAME, 2) && flags & ATTRS_REPORT) {
        if (updated)
            rprintf(FCLIENT, "%s\n", fname);
        else
            rprintf(FCLIENT, "%s is uptodate\n", fname);
    }
  cleanup:
    if (sxp == &sx2)
        free_stat_x(&sx2);
    return updated;
}

如果您的 Win7 计算机上有备用 space,请尝试以下操作:

  1. rsync 将您想要的文件同步到同一台计算机上的临时位置。因为是同一台电脑所以UID/GID应该设置成功。

  2. 在副本中执行您的find/chown脚本来为all[=30=设置UID/GID ] 文件。

  3. rsync 将副本返回到原始位置(小心!)文件的内容应该没有改变了,所以 rsync 应该做的唯一改变是设置 UID/GID.

确保使用 -aHAX 进行复制,并在覆盖任何内容之前进行干燥 运行!

我找到了两个解决基本问题的实用解决方案:

  1. 如果源环境和目标环境都使用 rsync 3.1.0 或更新版本,则有可用的新选项。在那种情况下,我可以将这些选项添加到我的 rsync 命令中:

    --usermap=4294967295:544 --groupmap=4294967295:197121

    感谢 Wayne Davison 指导我选择这些新选项!

  2. 如果您在目的地使用较旧的 rsync(就像我的 WD MyCloud 一样),您可以使用 cygwin 修改 rsync 源代码,如下所示。

    确保你的 cygwin 安装了 gcc gcc-core perl makequilt

    rsync site

    下载最新的 rsync 源 tar 文件

    我将其解压缩到我的 ~ 目录中的一个文件夹中。

    我下载了 Eclipse 作为 IDE 使用,但您可以使用 NotePad++ 修改文件,如下所示:

    在 main.c 文件中,我添加了一个信息行,您每次 运行 rsync 时都会看到该信息行,因此您知道您正在使用个人 rsync 版本。我确信还有一种适当的方法可以将版本号设置为我自己的版本号,但我会让别人评论如何做到这一点。 (我所有的行都以 /* dalek */ 结尾):

    starttime = time(NULL);
    our_uid = MY_UID();
    our_gid = MY_GID();
    
    rprintf(FINFO,"rsync 3.1.1 with edits started by uid: %u gid: %u\n", our_uid, our_gid ); /* dalek */
    

    然后,在flist.c中添加我的 /* dalek */ 行如下:

    if (!preserve_uid || ((uid_t)F_OWNER(file) == uid && *lastname))
        xflags |= XMIT_SAME_UID;
    else {
        uid = F_OWNER(file);
    
        if (uid==4294967295){                                                                      /* dalek */
            if (do_lchown(fname, our_uid, F_GROUP(file)) != 0) {                                   /* dalek */
                rprintf(FINFO, "COULD NOT CHANGE 4294967295 UID to %u on %s\n",our_uid,fname);     /* dalek */
            }else{                                                                                 /* dalek */
                uid=our_uid;                                                                       /* dalek */
            }                                                                                      /* dalek */
        }                                                                                          /* dalek */
    
        if (!numeric_ids) {
            user_name = add_uid(uid);
            if (inc_recurse && user_name)
                xflags |= XMIT_USER_NAME_FOLLOWS;
        }
    }
    if (!preserve_gid || ((gid_t)F_GROUP(file) == gid && *lastname))
        xflags |= XMIT_SAME_GID;
    else {
        gid = F_GROUP(file);
    
        if (gid==4294967295){                                                                      /* dalek */
            if (do_lchown(fname, F_OWNER(file), our_gid) != 0) {                                   /* dalek */
                rprintf(FINFO, "COULD NOT CHANGE 4294967295 GID to %u on %s\n",our_gid,fname);     /* dalek */
            }else{                                                                                 /* dalek */
                gid=our_gid;                                                                       /* dalek */
            }                                                                                      /* dalek */
        }                                                                                          /* dalek */
    
        if (!numeric_ids) {
            group_name = add_gid(gid);
            if (inc_recurse && group_name)
                xflags |= XMIT_GROUP_NAME_FOLLOWS;
        }
    }
    

    然后在你最近添加的rsync源目录运行./configure.sh然后运行make然后运行make install。这就对了!您应该在 .../usr/local/bin 中有一个新的 rsync.exe 文件,从现在开始只要您使用 rsync 就会 运行 因为 cygwin 将 .../usr/local/bin 放在 .. ./bin 在它使用的 PATH 中。原来的rsync还在.../bin。要使用原始文件,只需将修改后的 rsync.exe 移出 .../usr/local/bin.