Franz Schwartau
2012-May-07 14:52 UTC
Solved problem with hard links and schg flag under FreeBSD
Hi! Using rsync under FreeBSD with hard links and files having schg set result in EPERM "Operation not permitted". This behavior can be observed if rsyncing /usr/bin/. The patch fileflags.diff tries to deal with this situation but changes the flags of the parent directory only. It doesn't change the flags of the files itself. do_link() in syscall.c has to be fixed. The attached syscall-do_link.c.txt contains the complete function do_link(). patch-syscall.c.txt is a patch which have the be applied after fileflags.diff. Please have a look at the changes. What is the "official" way of asking for inclusion in the rsync distribution? Reporting a bug via bugzilla? Best regards Franz -------------- next part -------------- #ifdef HAVE_LINK int do_link(const char *fname1, const char *fname2) { if (dry_run) return 0; RETURN_ERROR_IF_RO_OR_LO; if (link(fname1, fname2) == 0) return 0; #ifdef SUPPORT_FORCE_CHANGE if (force_change && (errno == EPERM || errno == EACCES)) { char parent[MAXPATHLEN]; int parent_flags; int saved_errno = errno; int file_flags = make_mutable(fname1, NULL, NO_FFLAGS, force_change); if (file_flags) { int ret = link(fname1, fname2); undo_make_mutable(fname1, file_flags); if (ret == 0) return 0; } parent_flags = make_parentdir_mutable(fname2, force_change, parent, sizeof parent); if (parent_flags) { int ret = link(fname1, fname2); undo_make_mutable(parent, parent_flags); if (ret == 0) return 0; } errno = saved_errno; } #endif return -1; } #endif -------------- next part -------------- --- syscall.c.orig 2012-05-07 16:30:28.000000000 +0200 +++ syscall.c 2012-05-07 16:30:44.000000000 +0200 @@ -114,8 +114,16 @@ #ifdef SUPPORT_FORCE_CHANGE if (force_change && (errno == EPERM || errno == EACCES)) { char parent[MAXPATHLEN]; + int parent_flags; int saved_errno = errno; - int parent_flags = make_parentdir_mutable(fname2, force_change, parent, sizeof parent); + int file_flags = make_mutable(fname1, NULL, NO_FFLAGS, force_change); + if (file_flags) { + int ret = link(fname1, fname2); + undo_make_mutable(fname1, file_flags); + if (ret == 0) + return 0; + } + parent_flags = make_parentdir_mutable(fname2, force_change, parent, sizeof parent); if (parent_flags) { int ret = link(fname1, fname2); undo_make_mutable(parent, parent_flags);