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

时间:2015-12-10 15:05:29

标签: linux cygwin rsync

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

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

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

我试过这个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保护其隐藏/ sys文件的方法之一是将其uid和gid设置为4294967295(例如c:\ pagefil.sys和c:\ hiberfil.sys)。我已在rsync排除文件中排除了这两个文件,我想单独保留其Win7端的uid / gid。 find命令会chown他们。

我还尝试解析ls列表,这可能有用,但速度非常慢。由于我只处理Win7文件,我认为ls适合解析。

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

另一种更精确的方法,但速度慢,需要更困难的脚本,就是解析一个rsync --dry-run ...列表,找出哪些项目需要chowning

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

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

任何人都可以总结一下我在Win7计算机上编译rsync源代码所需的工具吗?

这是来自源代码的rsync.c(搜索'无法设置'):

    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;
}

2 个答案:

答案 0 :(得分:1)

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

  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。

答案 1 :(得分:0)

如果您的Win7计算机上有空余空间,请尝试以下操作:

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

  2. 在副本中,执行find / chown脚本,为所有文件设置UID / GID。

  3. rsync将复制复制到原始位置(小心!)的文件内容已更改,因此rsync应该进行的唯一更改将是设置UID / GID。

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