mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-05-24 06:45:27 -04:00
The ACL support has arrived! This version has a brand new protocol
that makes it incompatible with all prior versions. A patch will be provided to allow talking with older (patched) rsync versions.
This commit is contained in:
@@ -26,15 +26,15 @@ VERSION=@VERSION@
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .o
|
||||
|
||||
HEADERS=byteorder.h config.h errcode.h proto.h rsync.h lib/pool_alloc.h
|
||||
HEADERS=byteorder.h config.h errcode.h proto.h rsync.h smb_acls.h lib/pool_alloc.h
|
||||
LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o \
|
||||
lib/permstring.o lib/pool_alloc.o @LIBOBJS@
|
||||
lib/permstring.o lib/pool_alloc.o lib/sysacls.o @LIBOBJS@
|
||||
ZLIBOBJ=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \
|
||||
zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o
|
||||
OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
|
||||
util.o main.o checksum.o match.o syscall.o log.o backup.o
|
||||
OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o \
|
||||
fileio.o batch.o clientname.o chmod.o
|
||||
fileio.o batch.o clientname.o chmod.o acls.o
|
||||
OBJS3=progress.o pipe.o
|
||||
DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
|
||||
popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
|
||||
|
||||
39
backup.c
39
backup.c
@@ -22,6 +22,7 @@
|
||||
|
||||
extern int verbose;
|
||||
extern int am_root;
|
||||
extern int preserve_acls;
|
||||
extern int preserve_devices;
|
||||
extern int preserve_specials;
|
||||
extern int preserve_links;
|
||||
@@ -92,7 +93,8 @@ path
|
||||
****************************************************************************/
|
||||
static int make_bak_dir(char *fullpath)
|
||||
{
|
||||
STRUCT_STAT st;
|
||||
statx sx;
|
||||
struct file_struct *file;
|
||||
char *rel = fullpath + backup_dir_len;
|
||||
char *end = rel + strlen(rel);
|
||||
char *p = end;
|
||||
@@ -124,15 +126,25 @@ static int make_bak_dir(char *fullpath)
|
||||
if (p >= rel) {
|
||||
/* Try to transfer the directory settings of the
|
||||
* actual dir that the files are coming from. */
|
||||
if (do_stat(rel, &st) < 0) {
|
||||
if (do_stat(rel, &sx.st) < 0) {
|
||||
rsyserr(FERROR, errno,
|
||||
"make_bak_dir stat %s failed",
|
||||
full_fname(rel));
|
||||
} else {
|
||||
do_lchown(fullpath, st.st_uid, st.st_gid);
|
||||
#ifdef HAVE_CHMOD
|
||||
do_chmod(fullpath, st.st_mode);
|
||||
#ifdef SUPPORT_ACLS
|
||||
sx.acc_acl = sx.def_acl = NULL;
|
||||
#endif
|
||||
if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS)))
|
||||
continue;
|
||||
#ifdef SUPPORT_ACLS
|
||||
if (preserve_acls && !S_ISLNK(file->mode)) {
|
||||
get_acl(rel, &sx);
|
||||
cache_acl(file, &sx);
|
||||
free_acl(&sx);
|
||||
}
|
||||
#endif
|
||||
set_file_attrs(fullpath, file, NULL, 0);
|
||||
free(file);
|
||||
}
|
||||
}
|
||||
*p = '/';
|
||||
@@ -170,15 +182,18 @@ static int robust_move(const char *src, char *dst)
|
||||
* We will move the file to be deleted into a parallel directory tree. */
|
||||
static int keep_backup(const char *fname)
|
||||
{
|
||||
STRUCT_STAT st;
|
||||
statx sx;
|
||||
struct file_struct *file;
|
||||
char *buf;
|
||||
int kept = 0;
|
||||
int ret_code;
|
||||
|
||||
/* return if no file to keep */
|
||||
if (do_lstat(fname, &st) < 0)
|
||||
if (do_lstat(fname, &sx.st) < 0)
|
||||
return 1;
|
||||
#ifdef SUPPORT_ACLS
|
||||
sx.acc_acl = sx.def_acl = NULL;
|
||||
#endif
|
||||
|
||||
if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS)))
|
||||
return 1; /* the file could have disappeared */
|
||||
@@ -188,6 +203,14 @@ static int keep_backup(const char *fname)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_ACLS
|
||||
if (preserve_acls && !S_ISLNK(file->mode)) {
|
||||
get_acl(fname, &sx);
|
||||
cache_acl(file, &sx);
|
||||
free_acl(&sx);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Check to see if this is a device file, or link */
|
||||
if ((am_root && preserve_devices && IS_DEVICE(file->mode))
|
||||
|| (preserve_specials && IS_SPECIAL(file->mode))) {
|
||||
@@ -259,7 +282,7 @@ static int keep_backup(const char *fname)
|
||||
if (robust_move(fname, buf) != 0) {
|
||||
rsyserr(FERROR, errno, "keep_backup failed: %s -> \"%s\"",
|
||||
full_fname(fname), buf);
|
||||
} else if (st.st_nlink > 1) {
|
||||
} else if (sx.st.st_nlink > 1) {
|
||||
/* If someone has hard-linked the file into the backup
|
||||
* dir, rename() might return success but do nothing! */
|
||||
robust_unlink(fname); /* Just in case... */
|
||||
|
||||
@@ -36,7 +36,8 @@
|
||||
|
||||
/* Our 64-bit numbers are sent in MSB-first order so that we can use
|
||||
* the highest bits to indicate the number of bytes sent. */
|
||||
#define NVAL2(b,m) ((UVAL(b,0)&~(m))<<8|UVAL(b,1))
|
||||
#define NVAL1(b,m) (UVAL(b,0)&~(uint32)(m))
|
||||
#define NVAL2(b,m) (NVAL1(b,m)<<8|UVAL(b,1))
|
||||
#define NVAL3(b,m) (NVAL2(b,m)<<8|UVAL(b,2))
|
||||
#define NVAL4(b,m) (NVAL3(b,m)<<8|UVAL(b,3))
|
||||
#define NVAL5(b,m) ((int64)NVAL4(b,m)<<8|UVAL(b,4))
|
||||
|
||||
10
compat.c
10
compat.c
@@ -41,6 +41,7 @@ extern int prune_empty_dirs;
|
||||
extern int protocol_version;
|
||||
extern int preserve_uid;
|
||||
extern int preserve_gid;
|
||||
extern int preserve_acls;
|
||||
extern int preserve_hard_links;
|
||||
extern int need_messages_from_generator;
|
||||
extern int delete_mode, delete_before, delete_during, delete_after;
|
||||
@@ -61,6 +62,8 @@ void setup_protocol(int f_out,int f_in)
|
||||
preserve_uid = ++file_extra_cnt;
|
||||
if (preserve_gid)
|
||||
preserve_gid = ++file_extra_cnt;
|
||||
if (preserve_acls && !am_sender)
|
||||
preserve_acls = ++file_extra_cnt;
|
||||
|
||||
if (remote_protocol == 0) {
|
||||
if (!read_batch)
|
||||
@@ -108,6 +111,13 @@ void setup_protocol(int f_out,int f_in)
|
||||
protocol_version);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
if (preserve_acls) {
|
||||
rprintf(FERROR,
|
||||
"--acls requires protocol 30 or higher"
|
||||
" (negotiated %d).\n",
|
||||
protocol_version);
|
||||
exit_cleanup(RERR_PROTOCOL);
|
||||
}
|
||||
}
|
||||
|
||||
if (delete_mode && !(delete_before+delete_during+delete_after)) {
|
||||
|
||||
75
configure.in
75
configure.in
@@ -542,6 +542,11 @@ if test x"$ac_cv_func_strcasecmp" = x"no"; then
|
||||
AC_CHECK_LIB(resolv, strcasecmp)
|
||||
fi
|
||||
|
||||
AC_CHECK_FUNCS(aclsort)
|
||||
if test x"$ac_cv_func_aclsort" = x"no"; then
|
||||
AC_CHECK_LIB(sec, aclsort)
|
||||
fi
|
||||
|
||||
dnl At the moment we don't test for a broken memcmp(), because all we
|
||||
dnl need to do is test for equality, not comparison, and it seems that
|
||||
dnl every platform has a memcmp that can do at least that.
|
||||
@@ -806,6 +811,76 @@ AC_SUBST(OBJ_RESTORE)
|
||||
AC_SUBST(CC_SHOBJ_FLAG)
|
||||
AC_SUBST(BUILD_POPT)
|
||||
|
||||
AC_CHECK_HEADERS(sys/acl.h acl/libacl.h)
|
||||
AC_CHECK_FUNCS(_acl __acl _facl __facl)
|
||||
#################################################
|
||||
# check for ACL support
|
||||
|
||||
AC_MSG_CHECKING([whether to support ACLs])
|
||||
AC_ARG_ENABLE(acl-support,
|
||||
AC_HELP_STRING([--disable-acl-support],
|
||||
[Turn off ACL support]))
|
||||
|
||||
if test x"$enable_acl_support" = x"no"; then
|
||||
AC_MSG_RESULT(no)
|
||||
else
|
||||
AC_DEFINE(SUPPORT_ACLS, 1, [Define to 1 to add support for ACLs])
|
||||
case "$host_os" in
|
||||
*sysv5*)
|
||||
AC_MSG_RESULT(Using UnixWare ACLs)
|
||||
AC_DEFINE(HAVE_UNIXWARE_ACLS, 1, [true if you have UnixWare ACLs])
|
||||
;;
|
||||
*solaris*|*cygwin*)
|
||||
AC_MSG_RESULT(Using solaris ACLs)
|
||||
AC_DEFINE(HAVE_SOLARIS_ACLS, 1, [true if you have solaris ACLs])
|
||||
;;
|
||||
*hpux*)
|
||||
AC_MSG_RESULT(Using HPUX ACLs)
|
||||
AC_DEFINE(HAVE_HPUX_ACLS, 1, [true if you have HPUX ACLs])
|
||||
;;
|
||||
*irix*)
|
||||
AC_MSG_RESULT(Using IRIX ACLs)
|
||||
AC_DEFINE(HAVE_IRIX_ACLS, 1, [true if you have IRIX ACLs])
|
||||
;;
|
||||
*aix*)
|
||||
AC_MSG_RESULT(Using AIX ACLs)
|
||||
AC_DEFINE(HAVE_AIX_ACLS, 1, [true if you have AIX ACLs])
|
||||
;;
|
||||
*osf*)
|
||||
AC_MSG_RESULT(Using Tru64 ACLs)
|
||||
AC_DEFINE(HAVE_TRU64_ACLS, 1, [true if you have Tru64 ACLs])
|
||||
LIBS="$LIBS -lpacl"
|
||||
;;
|
||||
*)
|
||||
AC_MSG_RESULT(running tests:)
|
||||
AC_CHECK_LIB(acl,acl_get_file)
|
||||
AC_CACHE_CHECK([for ACL support],samba_cv_HAVE_POSIX_ACLS,[
|
||||
AC_TRY_LINK([#include <sys/types.h>
|
||||
#include <sys/acl.h>],
|
||||
[ acl_t acl; int entry_id; acl_entry_t *entry_p; return acl_get_entry( acl, entry_id, entry_p);],
|
||||
samba_cv_HAVE_POSIX_ACLS=yes,samba_cv_HAVE_POSIX_ACLS=no)])
|
||||
AC_MSG_CHECKING(ACL test results)
|
||||
if test x"$samba_cv_HAVE_POSIX_ACLS" = x"yes"; then
|
||||
AC_MSG_RESULT(Using posix ACLs)
|
||||
AC_DEFINE(HAVE_POSIX_ACLS, 1, [true if you have posix ACLs])
|
||||
AC_CACHE_CHECK([for acl_get_perm_np],samba_cv_HAVE_ACL_GET_PERM_NP,[
|
||||
AC_TRY_LINK([#include <sys/types.h>
|
||||
#include <sys/acl.h>],
|
||||
[ acl_permset_t permset_d; acl_perm_t perm; return acl_get_perm_np( permset_d, perm);],
|
||||
samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_cv_HAVE_ACL_GET_PERM_NP=no)])
|
||||
if test x"$samba_cv_HAVE_ACL_GET_PERM_NP" = x"yes"; then
|
||||
AC_DEFINE(HAVE_ACL_GET_PERM_NP, 1, [true if you have acl_get_perm_np])
|
||||
fi
|
||||
else
|
||||
AC_MSG_ERROR(Failed to find ACL support)
|
||||
if test x"$enable_acl_support" != x"yes"; then
|
||||
AC_DEFINE(SUPPORT_ACLS, 0)
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig])
|
||||
AC_OUTPUT
|
||||
|
||||
|
||||
55
flist.c
55
flist.c
@@ -41,6 +41,7 @@ extern int filesfrom_fd;
|
||||
extern int one_file_system;
|
||||
extern int copy_dirlinks;
|
||||
extern int keep_dirlinks;
|
||||
extern int preserve_acls;
|
||||
extern int preserve_links;
|
||||
extern int preserve_hard_links;
|
||||
extern int preserve_devices;
|
||||
@@ -152,6 +153,8 @@ static void list_file_entry(struct file_struct *f)
|
||||
permstring(permbuf, f->mode);
|
||||
len = F_LENGTH(f);
|
||||
|
||||
/* TODO: indicate '+' if the entry has an ACL. */
|
||||
|
||||
#ifdef SUPPORT_LINKS
|
||||
if (preserve_links && S_ISLNK(f->mode)) {
|
||||
rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
|
||||
@@ -464,13 +467,13 @@ static void send_file_entry(int f, struct file_struct *file, int ndx)
|
||||
if (flags & XMIT_SAME_NAME)
|
||||
write_byte(f, l1);
|
||||
if (flags & XMIT_LONG_NAME)
|
||||
write_int(f, l2);
|
||||
write_abbrevint30(f, l2);
|
||||
else
|
||||
write_byte(f, l2);
|
||||
write_buf(f, fname + l1, l2);
|
||||
|
||||
if (first_hlink_ndx >= 0) {
|
||||
write_int(f, first_hlink_ndx);
|
||||
write_abbrevint30(f, first_hlink_ndx);
|
||||
goto the_end;
|
||||
}
|
||||
|
||||
@@ -480,7 +483,7 @@ static void send_file_entry(int f, struct file_struct *file, int ndx)
|
||||
if (!(flags & XMIT_SAME_MODE))
|
||||
write_int(f, to_wire_mode(mode));
|
||||
if (preserve_uid && !(flags & XMIT_SAME_UID)) {
|
||||
write_int(f, uid);
|
||||
write_abbrevint30(f, uid);
|
||||
if (flags & XMIT_USER_NAME_FOLLOWS) {
|
||||
int len = strlen(user_name);
|
||||
write_byte(f, len);
|
||||
@@ -488,7 +491,7 @@ static void send_file_entry(int f, struct file_struct *file, int ndx)
|
||||
}
|
||||
}
|
||||
if (preserve_gid && !(flags & XMIT_SAME_GID)) {
|
||||
write_int(f, gid);
|
||||
write_abbrevint30(f, gid);
|
||||
if (flags & XMIT_GROUP_NAME_FOLLOWS) {
|
||||
int len = strlen(group_name);
|
||||
write_byte(f, len);
|
||||
@@ -514,7 +517,7 @@ static void send_file_entry(int f, struct file_struct *file, int ndx)
|
||||
if (preserve_links && S_ISLNK(mode)) {
|
||||
const char *sl = F_SYMLINK(file);
|
||||
int len = strlen(sl);
|
||||
write_int(f, len);
|
||||
write_abbrevint30(f, len);
|
||||
write_buf(f, sl, len);
|
||||
}
|
||||
#endif
|
||||
@@ -580,7 +583,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
|
||||
l1 = read_byte(f);
|
||||
|
||||
if (flags & XMIT_LONG_NAME)
|
||||
l2 = read_int(f);
|
||||
l2 = read_abbrevint30(f);
|
||||
else
|
||||
l2 = read_byte(f);
|
||||
|
||||
@@ -619,7 +622,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
|
||||
if (protocol_version >= 30
|
||||
&& BITS_SETnUNSET(flags, XMIT_HLINKED, XMIT_HLINK_FIRST)) {
|
||||
struct file_struct *first;
|
||||
first_hlink_ndx = read_int(f);
|
||||
first_hlink_ndx = read_abbrevint30(f);
|
||||
if (first_hlink_ndx < 0 || first_hlink_ndx >= flist->count) {
|
||||
rprintf(FERROR,
|
||||
"hard-link reference out of range: %d (%d)\n",
|
||||
@@ -657,14 +660,14 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
|
||||
mode = tweak_mode(mode, chmod_modes);
|
||||
|
||||
if (preserve_uid && !(flags & XMIT_SAME_UID)) {
|
||||
uid = (uid_t)read_int(f);
|
||||
uid = (uid_t)read_abbrevint30(f);
|
||||
if (flags & XMIT_USER_NAME_FOLLOWS)
|
||||
uid = recv_user_name(f, uid);
|
||||
else if (inc_recurse && am_root && !numeric_ids)
|
||||
uid = match_uid(uid);
|
||||
}
|
||||
if (preserve_gid && !(flags & XMIT_SAME_GID)) {
|
||||
gid = (gid_t)read_int(f);
|
||||
gid = (gid_t)read_abbrevint30(f);
|
||||
if (flags & XMIT_GROUP_NAME_FOLLOWS)
|
||||
gid = recv_group_name(f, gid);
|
||||
else if (inc_recurse && (!am_root || !numeric_ids))
|
||||
@@ -693,7 +696,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
|
||||
|
||||
#ifdef SUPPORT_LINKS
|
||||
if (preserve_links && S_ISLNK(mode)) {
|
||||
linkname_len = read_int(f) + 1; /* count the '\0' */
|
||||
linkname_len = read_abbrevint30(f) + 1; /* count the '\0' */
|
||||
if (linkname_len <= 0 || linkname_len > MAXPATHLEN) {
|
||||
rprintf(FERROR, "overflow: linkname_len=%d\n",
|
||||
linkname_len - 1);
|
||||
@@ -714,6 +717,12 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SUPPORT_ACLS
|
||||
/* We need one or two index int32s when we're preserving ACLs. */
|
||||
if (preserve_acls)
|
||||
extra_len += (S_ISDIR(mode) ? 2 : 1) * EXTRA_LEN;
|
||||
#endif
|
||||
|
||||
if (always_checksum && S_ISREG(mode))
|
||||
extra_len += SUM_EXTRA_CNT * EXTRA_LEN;
|
||||
|
||||
@@ -851,6 +860,11 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
|
||||
read_buf(f, bp, checksum_len);
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_ACLS
|
||||
if (preserve_acls && !S_ISLNK(mode))
|
||||
receive_acl(file, f);
|
||||
#endif
|
||||
|
||||
if (S_ISREG(mode) || S_ISLNK(mode))
|
||||
stats.total_size += file_length;
|
||||
|
||||
@@ -1122,6 +1136,9 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
|
||||
int flags, int filter_flags)
|
||||
{
|
||||
struct file_struct *file;
|
||||
#ifdef SUPPORT_ACLS
|
||||
statx sx;
|
||||
#endif
|
||||
|
||||
file = make_file(fname, flist, stp, flags, filter_flags);
|
||||
if (!file)
|
||||
@@ -1130,12 +1147,28 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
|
||||
if (chmod_modes && !S_ISLNK(file->mode))
|
||||
file->mode = tweak_mode(file->mode, chmod_modes);
|
||||
|
||||
#ifdef SUPPORT_ACLS
|
||||
if (preserve_acls && !S_ISLNK(file->mode) && f >= 0) {
|
||||
sx.st.st_mode = file->mode;
|
||||
sx.acc_acl = sx.def_acl = NULL;
|
||||
if (get_acl(fname, &sx) < 0)
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
maybe_emit_filelist_progress(flist->count + flist_count_offset);
|
||||
|
||||
flist_expand(flist);
|
||||
flist->files[flist->count++] = file;
|
||||
if (f >= 0)
|
||||
if (f >= 0) {
|
||||
send_file_entry(f, file, flist->count - 1);
|
||||
#ifdef SUPPORT_ACLS
|
||||
if (preserve_acls && !S_ISLNK(file->mode)) {
|
||||
send_acl(&sx, f);
|
||||
free_acl(&sx);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
272
generator.c
272
generator.c
@@ -35,6 +35,7 @@ extern int do_progress;
|
||||
extern int relative_paths;
|
||||
extern int implied_dirs;
|
||||
extern int keep_dirlinks;
|
||||
extern int preserve_acls;
|
||||
extern int preserve_links;
|
||||
extern int preserve_devices;
|
||||
extern int preserve_specials;
|
||||
@@ -89,6 +90,7 @@ extern int force_delete;
|
||||
extern int one_file_system;
|
||||
extern struct stats stats;
|
||||
extern dev_t filesystem_dev;
|
||||
extern mode_t orig_umask;
|
||||
extern char *backup_dir;
|
||||
extern char *backup_suffix;
|
||||
extern int backup_suffix_len;
|
||||
@@ -511,22 +513,31 @@ static void do_delete_pass(struct file_list *flist)
|
||||
rprintf(FINFO, " \r");
|
||||
}
|
||||
|
||||
int unchanged_attrs(struct file_struct *file, STRUCT_STAT *st)
|
||||
int unchanged_attrs(const char *fname, struct file_struct *file, statx *sxp)
|
||||
{
|
||||
if (preserve_perms && !BITS_EQUAL(st->st_mode, file->mode, CHMOD_BITS))
|
||||
if (preserve_perms && !BITS_EQUAL(sxp->st.st_mode, file->mode, CHMOD_BITS))
|
||||
return 0;
|
||||
|
||||
if (am_root && preserve_uid && st->st_uid != F_UID(file))
|
||||
if (am_root && preserve_uid && sxp->st.st_uid != F_UID(file))
|
||||
return 0;
|
||||
|
||||
if (preserve_gid && F_GID(file) != GID_NONE && st->st_gid != F_GID(file))
|
||||
if (preserve_gid && F_GID(file) != GID_NONE && sxp->st.st_gid != F_GID(file))
|
||||
return 0;
|
||||
|
||||
#ifdef SUPPORT_ACLS
|
||||
if (preserve_acls && !S_ISLNK(file->mode)) {
|
||||
if (!ACL_READY(*sxp))
|
||||
get_acl(fname, sxp);
|
||||
if (set_acl(NULL, file, sxp) == 0)
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void itemize(struct file_struct *file, int ndx, int statret,
|
||||
STRUCT_STAT *st, int32 iflags, uchar fnamecmp_type,
|
||||
void itemize(const char *fname, struct file_struct *file, int ndx, int statret,
|
||||
statx *sxp, int32 iflags, uchar fnamecmp_type,
|
||||
const char *xname)
|
||||
{
|
||||
if (statret >= 0) { /* A from-dest-dir statret can == 1! */
|
||||
@@ -534,20 +545,28 @@ void itemize(struct file_struct *file, int ndx, int statret,
|
||||
: S_ISDIR(file->mode) ? !omit_dir_times
|
||||
: !S_ISLNK(file->mode);
|
||||
|
||||
if (S_ISREG(file->mode) && F_LENGTH(file) != st->st_size)
|
||||
if (S_ISREG(file->mode) && F_LENGTH(file) != sxp->st.st_size)
|
||||
iflags |= ITEM_REPORT_SIZE;
|
||||
if ((iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !keep_time
|
||||
&& !(iflags & ITEM_MATCHED)
|
||||
&& (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
|
||||
|| (keep_time && cmp_time(file->modtime, st->st_mtime) != 0))
|
||||
|| (keep_time && cmp_time(file->modtime, sxp->st.st_mtime) != 0))
|
||||
iflags |= ITEM_REPORT_TIME;
|
||||
if (!BITS_EQUAL(st->st_mode, file->mode, CHMOD_BITS))
|
||||
if (!BITS_EQUAL(sxp->st.st_mode, file->mode, CHMOD_BITS))
|
||||
iflags |= ITEM_REPORT_PERMS;
|
||||
if (preserve_uid && am_root && F_UID(file) != st->st_uid)
|
||||
if (preserve_uid && am_root && F_UID(file) != sxp->st.st_uid)
|
||||
iflags |= ITEM_REPORT_OWNER;
|
||||
if (preserve_gid && F_GID(file) != GID_NONE
|
||||
&& st->st_gid != F_GID(file))
|
||||
&& sxp->st.st_gid != F_GID(file))
|
||||
iflags |= ITEM_REPORT_GROUP;
|
||||
#ifdef SUPPORT_ACLS
|
||||
if (preserve_acls && !S_ISLNK(file->mode)) {
|
||||
if (!ACL_READY(*sxp))
|
||||
get_acl(fname, sxp);
|
||||
if (set_acl(NULL, file, sxp) == 0)
|
||||
iflags |= ITEM_REPORT_ACL;
|
||||
}
|
||||
#endif
|
||||
} else
|
||||
iflags |= ITEM_IS_NEW;
|
||||
|
||||
@@ -783,7 +802,7 @@ static int find_fuzzy(struct file_struct *file, struct file_list *dirlist)
|
||||
* handling the file, -1 if no dest-linking occurred, or a non-negative
|
||||
* value if we found an alternate basis file. */
|
||||
static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
|
||||
char *cmpbuf, STRUCT_STAT *stp, int itemizing,
|
||||
char *cmpbuf, statx *sxp, int itemizing,
|
||||
enum logcode code)
|
||||
{
|
||||
int best_match = -1;
|
||||
@@ -792,7 +811,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
|
||||
|
||||
do {
|
||||
pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
|
||||
if (link_stat(cmpbuf, stp, 0) < 0 || !S_ISREG(stp->st_mode))
|
||||
if (link_stat(cmpbuf, &sxp->st, 0) < 0 || !S_ISREG(sxp->st.st_mode))
|
||||
continue;
|
||||
switch (match_level) {
|
||||
case 0:
|
||||
@@ -800,16 +819,16 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
|
||||
match_level = 1;
|
||||
/* FALL THROUGH */
|
||||
case 1:
|
||||
if (!unchanged_file(cmpbuf, file, stp))
|
||||
if (!unchanged_file(cmpbuf, file, &sxp->st))
|
||||
continue;
|
||||
best_match = j;
|
||||
match_level = 2;
|
||||
/* FALL THROUGH */
|
||||
case 2:
|
||||
if (!unchanged_attrs(file, stp))
|
||||
if (!unchanged_attrs(cmpbuf, file, sxp))
|
||||
continue;
|
||||
if (always_checksum > 0 && preserve_times
|
||||
&& cmp_time(stp->st_mtime, file->modtime))
|
||||
&& cmp_time(sxp->st.st_mtime, file->modtime))
|
||||
continue;
|
||||
best_match = j;
|
||||
match_level = 3;
|
||||
@@ -824,7 +843,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
|
||||
if (j != best_match) {
|
||||
j = best_match;
|
||||
pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
|
||||
if (link_stat(cmpbuf, stp, 0) < 0)
|
||||
if (link_stat(cmpbuf, &sxp->st, 0) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -834,16 +853,16 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
|
||||
if (!hard_link_one(file, fname, cmpbuf, 1))
|
||||
goto try_a_copy;
|
||||
if (preserve_hard_links && F_IS_HLINKED(file))
|
||||
finish_hard_link(file, fname, stp, itemizing, code, j);
|
||||
finish_hard_link(file, fname, &sxp->st, itemizing, code, j);
|
||||
if (itemizing && (verbose > 1 || stdout_format_has_i > 1)) {
|
||||
itemize(file, ndx, 1, stp,
|
||||
itemize(fname, file, ndx, 1, sxp,
|
||||
ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS,
|
||||
0, "");
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
if (itemizing)
|
||||
itemize(file, ndx, 0, stp, 0, 0, NULL);
|
||||
itemize(fname, file, ndx, 0, sxp, 0, 0, NULL);
|
||||
if (verbose > 1 && maybe_ATTRS_REPORT)
|
||||
rprintf(FCLIENT, "%s is uptodate\n", fname);
|
||||
return -2;
|
||||
@@ -861,7 +880,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
|
||||
return -1;
|
||||
}
|
||||
if (itemizing)
|
||||
itemize(file, ndx, 0, stp, ITEM_LOCAL_CHANGE, 0, NULL);
|
||||
itemize(fname, file, ndx, 0, sxp, ITEM_LOCAL_CHANGE, 0, NULL);
|
||||
set_file_attrs(fname, file, NULL, 0);
|
||||
if (maybe_ATTRS_REPORT
|
||||
&& ((!itemizing && verbose && match_level == 2)
|
||||
@@ -872,7 +891,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
|
||||
}
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
if (preserve_hard_links && F_IS_HLINKED(file))
|
||||
finish_hard_link(file, fname, stp, itemizing, code, -1);
|
||||
finish_hard_link(file, fname, &sxp->st, itemizing, code, -1);
|
||||
#endif
|
||||
return -2;
|
||||
}
|
||||
@@ -884,7 +903,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
|
||||
* handling the file, or -1 if no dest-linking occurred, or a non-negative
|
||||
* value if we found an alternate basis file. */
|
||||
static int try_dests_non(struct file_struct *file, char *fname, int ndx,
|
||||
char *cmpbuf, STRUCT_STAT *stp, int itemizing,
|
||||
char *cmpbuf, statx *sxp, int itemizing,
|
||||
enum logcode code)
|
||||
{
|
||||
char lnk[MAXPATHLEN];
|
||||
@@ -917,24 +936,24 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
|
||||
|
||||
do {
|
||||
pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
|
||||
if (link_stat(cmpbuf, stp, 0) < 0)
|
||||
if (link_stat(cmpbuf, &sxp->st, 0) < 0)
|
||||
continue;
|
||||
switch (type) {
|
||||
case TYPE_DIR:
|
||||
if (!S_ISDIR(stp->st_mode))
|
||||
if (!S_ISDIR(sxp->st.st_mode))
|
||||
continue;
|
||||
break;
|
||||
case TYPE_SPECIAL:
|
||||
if (!IS_SPECIAL(stp->st_mode))
|
||||
if (!IS_SPECIAL(sxp->st.st_mode))
|
||||
continue;
|
||||
break;
|
||||
case TYPE_DEVICE:
|
||||
if (!IS_DEVICE(stp->st_mode))
|
||||
if (!IS_DEVICE(sxp->st.st_mode))
|
||||
continue;
|
||||
break;
|
||||
#ifdef SUPPORT_LINKS
|
||||
case TYPE_SYMLINK:
|
||||
if (!S_ISLNK(stp->st_mode))
|
||||
if (!S_ISLNK(sxp->st.st_mode))
|
||||
continue;
|
||||
break;
|
||||
#endif
|
||||
@@ -949,7 +968,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
|
||||
case TYPE_SPECIAL:
|
||||
case TYPE_DEVICE:
|
||||
devp = F_RDEV_P(file);
|
||||
if (stp->st_rdev != MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)))
|
||||
if (sxp->st.st_rdev != MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)))
|
||||
continue;
|
||||
break;
|
||||
#ifdef SUPPORT_LINKS
|
||||
@@ -966,7 +985,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
|
||||
match_level = 2;
|
||||
best_match = j;
|
||||
}
|
||||
if (unchanged_attrs(file, stp)) {
|
||||
if (unchanged_attrs(cmpbuf, file, sxp)) {
|
||||
match_level = 3;
|
||||
best_match = j;
|
||||
break;
|
||||
@@ -979,7 +998,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
|
||||
if (j != best_match) {
|
||||
j = best_match;
|
||||
pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
|
||||
if (link_stat(cmpbuf, stp, 0) < 0)
|
||||
if (link_stat(cmpbuf, &sxp->st, 0) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1010,7 +1029,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
|
||||
: ITEM_LOCAL_CHANGE
|
||||
+ (match_level == 3 ? ITEM_XNAME_FOLLOWS : 0);
|
||||
char *lp = match_level == 3 ? "" : NULL;
|
||||
itemize(file, ndx, 0, stp, chg + ITEM_MATCHED, 0, lp);
|
||||
itemize(fname, file, ndx, 0, sxp, chg + ITEM_MATCHED, 0, lp);
|
||||
}
|
||||
if (verbose > 1 && maybe_ATTRS_REPORT) {
|
||||
rprintf(FCLIENT, "%s%s is uptodate\n",
|
||||
@@ -1023,6 +1042,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
|
||||
}
|
||||
|
||||
static int phase = 0;
|
||||
static int dflt_perms;
|
||||
|
||||
/* Acts on the indicated item in cur_flist whose name is fname. If a dir,
|
||||
* make sure it exists, and has the right permissions/timestamp info. For
|
||||
@@ -1043,7 +1063,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
static int need_fuzzy_dirlist = 0;
|
||||
struct file_struct *fuzzy_file = NULL;
|
||||
int fd = -1, f_copy = -1;
|
||||
STRUCT_STAT st, real_st, partial_st;
|
||||
statx sx, real_sx;
|
||||
STRUCT_STAT partial_st;
|
||||
struct file_struct *back_file = NULL;
|
||||
int statret, real_ret, stat_errno;
|
||||
char *fnamecmp, *partialptr, *backupptr = NULL;
|
||||
@@ -1088,6 +1109,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
return;
|
||||
}
|
||||
}
|
||||
#ifdef SUPPORT_ACLS
|
||||
sx.acc_acl = sx.def_acl = NULL;
|
||||
#endif
|
||||
if (dry_run > 1) {
|
||||
if (fuzzy_dirlist) {
|
||||
flist_free(fuzzy_dirlist);
|
||||
@@ -1100,7 +1124,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
const char *dn = file->dirname ? file->dirname : ".";
|
||||
if (parent_dirname != dn && strcmp(parent_dirname, dn) != 0) {
|
||||
if (relative_paths && !implied_dirs
|
||||
&& do_stat(dn, &st) < 0
|
||||
&& do_stat(dn, &sx.st) < 0
|
||||
&& create_directory_path(fname) < 0) {
|
||||
rsyserr(FERROR, errno,
|
||||
"recv_generator: mkdir %s failed",
|
||||
@@ -1112,6 +1136,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
}
|
||||
if (fuzzy_basis)
|
||||
need_fuzzy_dirlist = 1;
|
||||
#ifdef SUPPORT_ACLS
|
||||
if (!preserve_perms)
|
||||
dflt_perms = default_perms_for_dir(dn);
|
||||
#endif
|
||||
}
|
||||
parent_dirname = dn;
|
||||
|
||||
@@ -1121,7 +1149,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
need_fuzzy_dirlist = 0;
|
||||
}
|
||||
|
||||
statret = link_stat(fname, &st,
|
||||
statret = link_stat(fname, &sx.st,
|
||||
keep_dirlinks && S_ISDIR(file->mode));
|
||||
stat_errno = errno;
|
||||
}
|
||||
@@ -1149,8 +1177,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
* file of that name and it is *not* a directory, then
|
||||
* we need to delete it. If it doesn't exist, then
|
||||
* (perhaps recursively) create it. */
|
||||
if (statret == 0 && !S_ISDIR(st.st_mode)) {
|
||||
if (delete_item(fname, st.st_mode, "directory", del_opts) != 0)
|
||||
if (statret == 0 && !S_ISDIR(sx.st.st_mode)) {
|
||||
if (delete_item(fname, sx.st.st_mode, "directory", del_opts) != 0)
|
||||
return;
|
||||
statret = -1;
|
||||
}
|
||||
@@ -1159,18 +1187,18 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
dry_run++;
|
||||
}
|
||||
real_ret = statret;
|
||||
real_st = st;
|
||||
real_sx = sx;
|
||||
if (new_root_dir) {
|
||||
if (*fname == '.' && fname[1] == '\0')
|
||||
statret = -1;
|
||||
new_root_dir = 0;
|
||||
}
|
||||
if (!preserve_perms) { /* See comment in non-dir code below. */
|
||||
file->mode = dest_mode(file->mode, st.st_mode,
|
||||
statret == 0);
|
||||
file->mode = dest_mode(file->mode, sx.st.st_mode,
|
||||
dflt_perms, statret == 0);
|
||||
}
|
||||
if (statret != 0 && basis_dir[0] != NULL) {
|
||||
int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &st,
|
||||
int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx,
|
||||
itemizing, code);
|
||||
if (j == -2) {
|
||||
itemizing = 0;
|
||||
@@ -1179,7 +1207,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
statret = 1;
|
||||
}
|
||||
if (itemizing && f_out != -1) {
|
||||
itemize(file, ndx, statret, &st,
|
||||
itemize(fname, file, ndx, statret, &sx,
|
||||
statret ? ITEM_LOCAL_CHANGE : 0, 0, NULL);
|
||||
}
|
||||
if (real_ret != 0 && do_mkdir(fname,file->mode) < 0 && errno != EEXIST) {
|
||||
@@ -1193,38 +1221,39 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
"*** Skipping any contents from this failed directory ***\n");
|
||||
missing_below = F_DEPTH(file);
|
||||
file->flags |= FLAG_MISSING_DIR;
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
if (set_file_attrs(fname, file, real_ret ? NULL : &real_st, 0)
|
||||
if (set_file_attrs(fname, file, real_ret ? NULL : &real_sx, 0)
|
||||
&& verbose && code != FNONE && f_out != -1)
|
||||
rprintf(code, "%s/\n", fname);
|
||||
if (real_ret != 0 && one_file_system)
|
||||
real_st.st_dev = filesystem_dev;
|
||||
real_sx.st.st_dev = filesystem_dev;
|
||||
if (inc_recurse) {
|
||||
if (one_file_system) {
|
||||
uint32 *devp = F_DIRDEV_P(file);
|
||||
DEV_MAJOR(devp) = major(real_st.st_dev);
|
||||
DEV_MINOR(devp) = minor(real_st.st_dev);
|
||||
DEV_MAJOR(devp) = major(real_sx.st.st_dev);
|
||||
DEV_MINOR(devp) = minor(real_sx.st.st_dev);
|
||||
}
|
||||
}
|
||||
else if (delete_during && f_out != -1 && !phase && dry_run < 2
|
||||
&& (file->flags & FLAG_XFER_DIR))
|
||||
delete_in_dir(cur_flist, fname, file, &real_st.st_dev);
|
||||
return;
|
||||
delete_in_dir(cur_flist, fname, file, &real_sx.st.st_dev);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* If we're not preserving permissions, change the file-list's
|
||||
* mode based on the local permissions and some heuristics. */
|
||||
if (!preserve_perms) {
|
||||
int exists = statret == 0 && !S_ISDIR(st.st_mode);
|
||||
file->mode = dest_mode(file->mode, st.st_mode, exists);
|
||||
int exists = statret == 0 && !S_ISDIR(sx.st.st_mode);
|
||||
file->mode = dest_mode(file->mode, sx.st.st_mode, dflt_perms,
|
||||
exists);
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
if (preserve_hard_links && F_HLINK_NOT_FIRST(file)
|
||||
&& hard_link_check(file, ndx, fname, statret, &st, itemizing, code))
|
||||
return;
|
||||
&& hard_link_check(file, ndx, fname, statret, &sx, itemizing, code))
|
||||
goto cleanup;
|
||||
#endif
|
||||
|
||||
if (preserve_links && S_ISLNK(file->mode)) {
|
||||
@@ -1244,28 +1273,28 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
char lnk[MAXPATHLEN];
|
||||
int len;
|
||||
|
||||
if (!S_ISLNK(st.st_mode))
|
||||
if (!S_ISLNK(sx.st.st_mode))
|
||||
statret = -1;
|
||||
else if ((len = readlink(fname, lnk, MAXPATHLEN-1)) > 0
|
||||
&& strncmp(lnk, sl, len) == 0 && sl[len] == '\0') {
|
||||
/* The link is pointing to the right place. */
|
||||
if (itemizing)
|
||||
itemize(file, ndx, 0, &st, 0, 0, NULL);
|
||||
set_file_attrs(fname, file, &st, maybe_ATTRS_REPORT);
|
||||
itemize(fname, file, ndx, 0, &sx, 0, 0, NULL);
|
||||
set_file_attrs(fname, file, &sx, maybe_ATTRS_REPORT);
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
if (preserve_hard_links && F_IS_HLINKED(file))
|
||||
finish_hard_link(file, fname, &st, itemizing, code, -1);
|
||||
finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
|
||||
#endif
|
||||
if (remove_source_files == 1)
|
||||
goto return_with_success;
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
/* Not the right symlink (or not a symlink), so
|
||||
* delete it. */
|
||||
if (delete_item(fname, st.st_mode, "symlink", del_opts) != 0)
|
||||
return;
|
||||
if (delete_item(fname, sx.st.st_mode, "symlink", del_opts) != 0)
|
||||
goto cleanup;
|
||||
} else if (basis_dir[0] != NULL) {
|
||||
int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &st,
|
||||
int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx,
|
||||
itemizing, code);
|
||||
if (j == -2) {
|
||||
#ifndef CAN_HARDLINK_SYMLINK
|
||||
@@ -1274,7 +1303,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
} else
|
||||
#endif
|
||||
if (!copy_dest)
|
||||
return;
|
||||
goto cleanup;
|
||||
itemizing = 0;
|
||||
code = FNONE;
|
||||
} else if (j >= 0)
|
||||
@@ -1282,7 +1311,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
}
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
if (preserve_hard_links && F_HLINK_NOT_LAST(file))
|
||||
return;
|
||||
goto cleanup;
|
||||
#endif
|
||||
if (do_symlink(sl, fname) != 0) {
|
||||
rsyserr(FERROR, errno, "symlink %s -> \"%s\" failed",
|
||||
@@ -1290,7 +1319,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
} else {
|
||||
set_file_attrs(fname, file, NULL, 0);
|
||||
if (itemizing) {
|
||||
itemize(file, ndx, statret, &st,
|
||||
itemize(fname, file, ndx, statret, &sx,
|
||||
ITEM_LOCAL_CHANGE, 0, NULL);
|
||||
}
|
||||
if (code != FNONE && verbose)
|
||||
@@ -1306,7 +1335,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
goto return_with_success;
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((am_root && preserve_devices && IS_DEVICE(file->mode))
|
||||
@@ -1316,33 +1345,33 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
if (statret == 0) {
|
||||
char *t;
|
||||
if (IS_DEVICE(file->mode)) {
|
||||
if (!IS_DEVICE(st.st_mode))
|
||||
if (!IS_DEVICE(sx.st.st_mode))
|
||||
statret = -1;
|
||||
t = "device file";
|
||||
} else {
|
||||
if (!IS_SPECIAL(st.st_mode))
|
||||
if (!IS_SPECIAL(sx.st.st_mode))
|
||||
statret = -1;
|
||||
t = "special file";
|
||||
}
|
||||
if (statret == 0
|
||||
&& BITS_EQUAL(st.st_mode, file->mode, _S_IFMT)
|
||||
&& st.st_rdev == rdev) {
|
||||
&& BITS_EQUAL(sx.st.st_mode, file->mode, _S_IFMT)
|
||||
&& sx.st.st_rdev == rdev) {
|
||||
/* The device or special file is identical. */
|
||||
if (itemizing)
|
||||
itemize(file, ndx, 0, &st, 0, 0, NULL);
|
||||
set_file_attrs(fname, file, &st, maybe_ATTRS_REPORT);
|
||||
itemize(fname, file, ndx, 0, &sx, 0, 0, NULL);
|
||||
set_file_attrs(fname, file, &sx, maybe_ATTRS_REPORT);
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
if (preserve_hard_links && F_IS_HLINKED(file))
|
||||
finish_hard_link(file, fname, &st, itemizing, code, -1);
|
||||
finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
|
||||
#endif
|
||||
if (remove_source_files == 1)
|
||||
goto return_with_success;
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
if (delete_item(fname, st.st_mode, t, del_opts) != 0)
|
||||
return;
|
||||
if (delete_item(fname, sx.st.st_mode, t, del_opts) != 0)
|
||||
goto cleanup;
|
||||
} else if (basis_dir[0] != NULL) {
|
||||
int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &st,
|
||||
int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx,
|
||||
itemizing, code);
|
||||
if (j == -2) {
|
||||
#ifndef CAN_HARDLINK_SPECIAL
|
||||
@@ -1351,7 +1380,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
} else
|
||||
#endif
|
||||
if (!copy_dest)
|
||||
return;
|
||||
goto cleanup;
|
||||
itemizing = 0;
|
||||
code = FNONE;
|
||||
} else if (j >= 0)
|
||||
@@ -1359,7 +1388,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
}
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
if (preserve_hard_links && F_HLINK_NOT_LAST(file))
|
||||
return;
|
||||
goto cleanup;
|
||||
#endif
|
||||
if (verbose > 2) {
|
||||
rprintf(FINFO, "mknod(%s, 0%o, [%ld,%ld])\n",
|
||||
@@ -1372,7 +1401,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
} else {
|
||||
set_file_attrs(fname, file, NULL, 0);
|
||||
if (itemizing) {
|
||||
itemize(file, ndx, statret, &st,
|
||||
itemize(fname, file, ndx, statret, &sx,
|
||||
ITEM_LOCAL_CHANGE, 0, NULL);
|
||||
}
|
||||
if (code != FNONE && verbose)
|
||||
@@ -1384,14 +1413,14 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
if (remove_source_files == 1)
|
||||
goto return_with_success;
|
||||
}
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!S_ISREG(file->mode)) {
|
||||
if (solo_file)
|
||||
fname = f_name(file, NULL);
|
||||
rprintf(FINFO, "skipping non-regular file \"%s\"\n", fname);
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (max_size > 0 && F_LENGTH(file) > max_size) {
|
||||
@@ -1400,7 +1429,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
fname = f_name(file, NULL);
|
||||
rprintf(FINFO, "%s is over max-size\n", fname);
|
||||
}
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
if (min_size > 0 && F_LENGTH(file) < min_size) {
|
||||
if (verbose > 1) {
|
||||
@@ -1408,39 +1437,39 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
fname = f_name(file, NULL);
|
||||
rprintf(FINFO, "%s is under min-size\n", fname);
|
||||
}
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (ignore_existing > 0 && statret == 0) {
|
||||
if (verbose > 1)
|
||||
rprintf(FINFO, "%s exists\n", fname);
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (update_only > 0 && statret == 0
|
||||
&& cmp_time(st.st_mtime, file->modtime) > 0) {
|
||||
&& cmp_time(sx.st.st_mtime, file->modtime) > 0) {
|
||||
if (verbose > 1)
|
||||
rprintf(FINFO, "%s is newer\n", fname);
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
fnamecmp = fname;
|
||||
fnamecmp_type = FNAMECMP_FNAME;
|
||||
|
||||
if (statret == 0 && !S_ISREG(st.st_mode)) {
|
||||
if (delete_item(fname, st.st_mode, "regular file", del_opts) != 0)
|
||||
return;
|
||||
if (statret == 0 && !S_ISREG(sx.st.st_mode)) {
|
||||
if (delete_item(fname, sx.st.st_mode, "regular file", del_opts) != 0)
|
||||
goto cleanup;
|
||||
statret = -1;
|
||||
stat_errno = ENOENT;
|
||||
}
|
||||
|
||||
if (statret != 0 && basis_dir[0] != NULL) {
|
||||
int j = try_dests_reg(file, fname, ndx, fnamecmpbuf, &st,
|
||||
int j = try_dests_reg(file, fname, ndx, fnamecmpbuf, &sx,
|
||||
itemizing, code);
|
||||
if (j == -2) {
|
||||
if (remove_source_files == 1)
|
||||
goto return_with_success;
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
if (j >= 0) {
|
||||
fnamecmp = fnamecmpbuf;
|
||||
@@ -1450,7 +1479,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
}
|
||||
|
||||
real_ret = statret;
|
||||
real_st = st;
|
||||
real_sx = sx;
|
||||
|
||||
if (partial_dir && (partialptr = partial_dir_fname(fname)) != NULL
|
||||
&& link_stat(partialptr, &partial_st, 0) == 0
|
||||
@@ -1469,7 +1498,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
rprintf(FINFO, "fuzzy basis selected for %s: %s\n",
|
||||
fname, fnamecmpbuf);
|
||||
}
|
||||
st.st_size = F_LENGTH(fuzzy_file);
|
||||
sx.st.st_size = F_LENGTH(fuzzy_file);
|
||||
statret = 0;
|
||||
fnamecmp = fnamecmpbuf;
|
||||
fnamecmp_type = FNAMECMP_FUZZY;
|
||||
@@ -1479,45 +1508,45 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
if (statret != 0) {
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
if (preserve_hard_links && F_HLINK_NOT_LAST(file))
|
||||
return;
|
||||
goto cleanup;
|
||||
#endif
|
||||
if (stat_errno == ENOENT)
|
||||
goto notify_others;
|
||||
rsyserr(FERROR, stat_errno, "recv_generator: failed to stat %s",
|
||||
full_fname(fname));
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (append_mode > 0 && st.st_size > F_LENGTH(file))
|
||||
return;
|
||||
if (append_mode > 0 && sx.st.st_size > F_LENGTH(file))
|
||||
goto cleanup;
|
||||
|
||||
if (fnamecmp_type <= FNAMECMP_BASIS_DIR_HIGH)
|
||||
;
|
||||
else if (fnamecmp_type == FNAMECMP_FUZZY)
|
||||
;
|
||||
else if (unchanged_file(fnamecmp, file, &st)) {
|
||||
else if (unchanged_file(fnamecmp, file, &sx.st)) {
|
||||
if (partialptr) {
|
||||
do_unlink(partialptr);
|
||||
handle_partial_dir(partialptr, PDIR_DELETE);
|
||||
}
|
||||
if (itemizing)
|
||||
itemize(file, ndx, statret, &st, 0, 0, NULL);
|
||||
set_file_attrs(fname, file, &st, maybe_ATTRS_REPORT);
|
||||
itemize(fnamecmp, file, ndx, statret, &sx, 0, 0, NULL);
|
||||
set_file_attrs(fname, file, &sx, maybe_ATTRS_REPORT);
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
if (preserve_hard_links && F_IS_HLINKED(file))
|
||||
finish_hard_link(file, fname, &st, itemizing, code, -1);
|
||||
finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
|
||||
#endif
|
||||
if (remove_source_files != 1)
|
||||
return;
|
||||
goto cleanup;
|
||||
return_with_success:
|
||||
if (!dry_run)
|
||||
send_msg_int(MSG_SUCCESS, ndx);
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
prepare_to_open:
|
||||
if (partialptr) {
|
||||
st = partial_st;
|
||||
sx.st = partial_st;
|
||||
fnamecmp = partialptr;
|
||||
fnamecmp_type = FNAMECMP_PARTIAL_DIR;
|
||||
statret = 0;
|
||||
@@ -1542,7 +1571,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
/* pretend the file didn't exist */
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
if (preserve_hard_links && F_HLINK_NOT_LAST(file))
|
||||
return;
|
||||
goto cleanup;
|
||||
#endif
|
||||
statret = real_ret = -1;
|
||||
goto notify_others;
|
||||
@@ -1551,7 +1580,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
if (inplace && make_backups > 0 && fnamecmp_type == FNAMECMP_FNAME) {
|
||||
if (!(backupptr = get_backup_name(fname))) {
|
||||
close(fd);
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) {
|
||||
close(fd);
|
||||
@@ -1562,7 +1591,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
full_fname(backupptr));
|
||||
unmake_file(back_file);
|
||||
close(fd);
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
if ((f_copy = do_open(backupptr,
|
||||
O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) {
|
||||
@@ -1570,14 +1599,14 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
full_fname(backupptr));
|
||||
unmake_file(back_file);
|
||||
close(fd);
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
fnamecmp_type = FNAMECMP_BACKUP;
|
||||
}
|
||||
|
||||
if (verbose > 3) {
|
||||
rprintf(FINFO, "gen mapped %s of size %.0f\n",
|
||||
fnamecmp, (double)st.st_size);
|
||||
fnamecmp, (double)sx.st.st_size);
|
||||
}
|
||||
|
||||
if (verbose > 2)
|
||||
@@ -1601,26 +1630,30 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
iflags |= ITEM_BASIS_TYPE_FOLLOWS;
|
||||
if (fnamecmp_type == FNAMECMP_FUZZY)
|
||||
iflags |= ITEM_XNAME_FOLLOWS;
|
||||
itemize(file, -1, real_ret, &real_st, iflags, fnamecmp_type,
|
||||
itemize(fnamecmp, file, -1, real_ret, &real_sx, iflags, fnamecmp_type,
|
||||
fuzzy_file ? fuzzy_file->basename : NULL);
|
||||
#ifdef SUPPORT_ACLS
|
||||
if (preserve_acls)
|
||||
free_acl(&real_sx);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!do_xfers) {
|
||||
#ifdef SUPPORT_HARD_LINKS
|
||||
if (preserve_hard_links && F_IS_HLINKED(file))
|
||||
finish_hard_link(file, fname, &st, itemizing, code, -1);
|
||||
finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
|
||||
#endif
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
if (read_batch)
|
||||
return;
|
||||
goto cleanup;
|
||||
|
||||
if (statret != 0 || whole_file) {
|
||||
write_sum_head(f_out, NULL);
|
||||
return;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
generate_and_send_sums(fd, st.st_size, f_out, f_copy);
|
||||
generate_and_send_sums(fd, sx.st.st_size, f_out, f_copy);
|
||||
|
||||
if (f_copy >= 0) {
|
||||
close(f_copy);
|
||||
@@ -1633,6 +1666,13 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
cleanup:
|
||||
#ifdef SUPPORT_ACLS
|
||||
if (preserve_acls)
|
||||
free_acl(&sx);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
static void touch_up_dirs(struct file_list *flist, int ndx)
|
||||
@@ -1803,6 +1843,8 @@ void generate_files(int f_out, const char *local_name)
|
||||
* notice that and let us know via the redo pipe (or its closing). */
|
||||
ignore_timeout = 1;
|
||||
|
||||
dflt_perms = (ACCESSPERMS & ~orig_umask);
|
||||
|
||||
do {
|
||||
if (inc_recurse && delete_during && cur_flist->ndx_start) {
|
||||
struct file_struct *fp = dir_flist->files[cur_flist->parent_ndx];
|
||||
|
||||
68
hlink.c
68
hlink.c
@@ -26,6 +26,7 @@ extern int verbose;
|
||||
extern int dry_run;
|
||||
extern int do_xfers;
|
||||
extern int link_dest;
|
||||
extern int preserve_acls;
|
||||
extern int make_backups;
|
||||
extern int protocol_version;
|
||||
extern int remove_source_files;
|
||||
@@ -267,15 +268,15 @@ void match_hard_links(void)
|
||||
}
|
||||
|
||||
static int maybe_hard_link(struct file_struct *file, int ndx,
|
||||
const char *fname, int statret, STRUCT_STAT *stp,
|
||||
const char *fname, int statret, statx *sxp,
|
||||
const char *oldname, STRUCT_STAT *old_stp,
|
||||
const char *realname, int itemizing, enum logcode code)
|
||||
{
|
||||
if (statret == 0) {
|
||||
if (stp->st_dev == old_stp->st_dev
|
||||
&& stp->st_ino == old_stp->st_ino) {
|
||||
if (sxp->st.st_dev == old_stp->st_dev
|
||||
&& sxp->st.st_ino == old_stp->st_ino) {
|
||||
if (itemizing) {
|
||||
itemize(file, ndx, statret, stp,
|
||||
itemize(fname, file, ndx, statret, sxp,
|
||||
ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS,
|
||||
0, "");
|
||||
}
|
||||
@@ -296,7 +297,7 @@ static int maybe_hard_link(struct file_struct *file, int ndx,
|
||||
|
||||
if (hard_link_one(file, fname, oldname, 0)) {
|
||||
if (itemizing) {
|
||||
itemize(file, ndx, statret, stp,
|
||||
itemize(fname, file, ndx, statret, sxp,
|
||||
ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS, 0,
|
||||
realname);
|
||||
}
|
||||
@@ -310,7 +311,7 @@ static int maybe_hard_link(struct file_struct *file, int ndx,
|
||||
/* Only called if FLAG_HLINKED is set and FLAG_HLINK_FIRST is not. Returns:
|
||||
* 0 = process the file, 1 = skip the file, -1 = error occurred. */
|
||||
int hard_link_check(struct file_struct *file, int ndx, const char *fname,
|
||||
int statret, STRUCT_STAT *stp, int itemizing,
|
||||
int statret, statx *sxp, int itemizing,
|
||||
enum logcode code)
|
||||
{
|
||||
STRUCT_STAT prev_st;
|
||||
@@ -361,18 +362,20 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
|
||||
if (statret < 0 && basis_dir[0] != NULL) {
|
||||
/* If we match an alt-dest item, we don't output this as a change. */
|
||||
char cmpbuf[MAXPATHLEN];
|
||||
STRUCT_STAT alt_st;
|
||||
statx alt_sx;
|
||||
int j = 0;
|
||||
#ifdef SUPPORT_ACLS
|
||||
alt_sx.acc_acl = alt_sx.def_acl = NULL;
|
||||
#endif
|
||||
do {
|
||||
pathjoin(cmpbuf, MAXPATHLEN, basis_dir[j], fname);
|
||||
if (link_stat(cmpbuf, &alt_st, 0) < 0)
|
||||
if (link_stat(cmpbuf, &alt_sx.st, 0) < 0)
|
||||
continue;
|
||||
if (link_dest) {
|
||||
if (prev_st.st_dev != alt_st.st_dev
|
||||
|| prev_st.st_ino != alt_st.st_ino)
|
||||
if (prev_st.st_dev != alt_sx.st.st_dev
|
||||
|| prev_st.st_ino != alt_sx.st.st_ino)
|
||||
continue;
|
||||
statret = 1;
|
||||
*stp = alt_st;
|
||||
if (verbose < 2 || !stdout_format_has_i) {
|
||||
itemizing = 0;
|
||||
code = FNONE;
|
||||
@@ -381,16 +384,32 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!unchanged_file(cmpbuf, file, &alt_st))
|
||||
if (!unchanged_file(cmpbuf, file, &alt_sx.st))
|
||||
continue;
|
||||
statret = 1;
|
||||
*stp = alt_st;
|
||||
if (unchanged_attrs(file, &alt_st))
|
||||
if (unchanged_attrs(cmpbuf, file, &alt_sx))
|
||||
break;
|
||||
} while (basis_dir[++j] != NULL);
|
||||
if (statret == 1) {
|
||||
sxp->st = alt_sx.st;
|
||||
#ifdef SUPPORT_ACLS
|
||||
if (preserve_acls && !S_ISLNK(file->mode)) {
|
||||
if (!ACL_READY(*sxp))
|
||||
get_acl(cmpbuf, sxp);
|
||||
else {
|
||||
sxp->acc_acl = alt_sx.acc_acl;
|
||||
sxp->def_acl = alt_sx.def_acl;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef SUPPORT_ACLS
|
||||
else if (preserve_acls)
|
||||
free_acl(&alt_sx);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (maybe_hard_link(file, ndx, fname, statret, stp, prev_name, &prev_st,
|
||||
if (maybe_hard_link(file, ndx, fname, statret, sxp, prev_name, &prev_st,
|
||||
realname, itemizing, code) < 0)
|
||||
return -1;
|
||||
|
||||
@@ -425,7 +444,8 @@ void finish_hard_link(struct file_struct *file, const char *fname,
|
||||
STRUCT_STAT *stp, int itemizing, enum logcode code,
|
||||
int alt_dest)
|
||||
{
|
||||
STRUCT_STAT st, prev_st;
|
||||
statx prev_sx;
|
||||
STRUCT_STAT st;
|
||||
char alt_name[MAXPATHLEN], *prev_name;
|
||||
const char *our_name;
|
||||
int prev_statret, ndx, prev_ndx = F_HL_PREV(file);
|
||||
@@ -449,14 +469,24 @@ void finish_hard_link(struct file_struct *file, const char *fname,
|
||||
} else
|
||||
our_name = fname;
|
||||
|
||||
#ifdef SUPPORT_ACLS
|
||||
prev_sx.acc_acl = prev_sx.def_acl = NULL;
|
||||
#endif
|
||||
|
||||
while ((ndx = prev_ndx) >= 0) {
|
||||
int val;
|
||||
file = FPTR(ndx);
|
||||
file->flags = (file->flags & ~FLAG_HLINK_FIRST) | FLAG_HLINK_DONE;
|
||||
prev_ndx = F_HL_PREV(file);
|
||||
prev_name = f_name(file, NULL);
|
||||
prev_statret = link_stat(prev_name, &prev_st, 0);
|
||||
if (maybe_hard_link(file, ndx, prev_name, prev_statret, &prev_st,
|
||||
our_name, stp, fname, itemizing, code) < 0)
|
||||
prev_statret = link_stat(prev_name, &prev_sx.st, 0);
|
||||
val = maybe_hard_link(file, ndx, prev_name, prev_statret, &prev_sx,
|
||||
our_name, stp, fname, itemizing, code);
|
||||
#ifdef SUPPORT_ACLS
|
||||
if (preserve_acls)
|
||||
free_acl(&prev_sx);
|
||||
#endif
|
||||
if (val < 0)
|
||||
continue;
|
||||
if (remove_source_files == 1 && do_xfers)
|
||||
send_msg_int(MSG_SUCCESS, ndx);
|
||||
|
||||
72
io.c
72
io.c
@@ -100,10 +100,10 @@ static int active_filecnt = 0;
|
||||
static OFF_T active_bytecnt = 0;
|
||||
|
||||
static char int_byte_cnt[64] = {
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* (00 - 3F)/4 */
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* (40 - 7F)/4 */
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, /* (80 - BF)/4 */
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 8, 9, /* (C0 - FF)/4 */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* (00 - 3F)/4 */
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* (40 - 7F)/4 */
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* (80 - BF)/4 */
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 6, 7, /* (C0 - FF)/4 */
|
||||
};
|
||||
|
||||
static void readfd(int fd, char *buffer, size_t N);
|
||||
@@ -998,6 +998,37 @@ unsigned short read_shortint(int f)
|
||||
return (UVAL(b, 1) << 8) + UVAL(b, 0);
|
||||
}
|
||||
|
||||
int32 read_abbrevint(int f)
|
||||
{
|
||||
int32 num;
|
||||
char b[5];
|
||||
int cnt;
|
||||
readfd(f, b, 1);
|
||||
cnt = int_byte_cnt[CVAL(b, 0) / 4];
|
||||
if (cnt > 1)
|
||||
readfd(f, b + 1, cnt - 1);
|
||||
switch (cnt) {
|
||||
case 1:
|
||||
num = NVAL1(b, 0);
|
||||
break;
|
||||
case 2:
|
||||
num = NVAL2(b, 0x80);
|
||||
break;
|
||||
case 3:
|
||||
num = NVAL3(b, 0xC0);
|
||||
break;
|
||||
case 4:
|
||||
num = NVAL4(b, 0xE0);
|
||||
break;
|
||||
case 5:
|
||||
num = NVAL4(b+1, 0);
|
||||
break;
|
||||
default:
|
||||
exit_cleanup(RERR_PROTOCOL); /* impossible... */
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
int32 read_int(int f)
|
||||
{
|
||||
char b[4];
|
||||
@@ -1033,7 +1064,7 @@ int64 read_longint(int f)
|
||||
} else {
|
||||
int cnt;
|
||||
readfd(f, b, 3);
|
||||
cnt = int_byte_cnt[CVAL(b, 0) / 4];
|
||||
cnt = int_byte_cnt[CVAL(b, 0) / 4] + 2;
|
||||
#if SIZEOF_INT64 < 8
|
||||
if (cnt > 5 || (cnt == 5 && (CVAL(b,0)&0x3F || CVAL(b,1)&0x80))) {
|
||||
rprintf(FERROR, "Integer overflow: attempted 64-bit offset\n");
|
||||
@@ -1373,6 +1404,37 @@ void write_shortint(int f, unsigned short x)
|
||||
writefd(f, b, 2);
|
||||
}
|
||||
|
||||
void write_abbrevint(int f, int32 x)
|
||||
{
|
||||
char b[5];
|
||||
if ((uint32)x < ((uint32)1<<(1*8-1))) {
|
||||
b[0] = (char)x;
|
||||
writefd(f, b, 1);
|
||||
} else if ((uint32)x < ((uint32)1<<(2*8-2))) {
|
||||
b[0] = (char)(x >> 8) | 0x80;
|
||||
b[1] = (char)x;
|
||||
writefd(f, b, 2);
|
||||
} else if ((uint32)x < ((uint32)1<<(3*8-3))) {
|
||||
b[0] = (char)(x >> 16) | 0xC0;
|
||||
b[1] = (char)(x >> 8);
|
||||
b[2] = (char)x;
|
||||
writefd(f, b, 3);
|
||||
} else if ((uint32)x < ((uint32)1<<(4*8-4))) {
|
||||
b[0] = (char)(x >> 24) | 0xE0;
|
||||
b[1] = (char)(x >> 16);
|
||||
b[2] = (char)(x >> 8);
|
||||
b[3] = (char)x;
|
||||
writefd(f, b, 4);
|
||||
} else {
|
||||
b[0] = 0xF0;
|
||||
b[1] = (char)(x >> 24);
|
||||
b[2] = (char)(x >> 16);
|
||||
b[3] = (char)(x >> 8);
|
||||
b[4] = (char)x;
|
||||
writefd(f, b, 5);
|
||||
}
|
||||
}
|
||||
|
||||
void write_int(int f, int32 x)
|
||||
{
|
||||
char b[4];
|
||||
|
||||
3251
lib/sysacls.c
Normal file
3251
lib/sysacls.c
Normal file
File diff suppressed because it is too large
Load Diff
40
lib/sysacls.h
Normal file
40
lib/sysacls.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifdef SUPPORT_ACLS
|
||||
|
||||
#ifdef HAVE_SYS_ACL_H
|
||||
#include <sys/acl.h>
|
||||
#endif
|
||||
#ifdef HAVE_ACL_LIBACL_H
|
||||
#include <acl/libacl.h>
|
||||
#endif
|
||||
#include "smb_acls.h"
|
||||
|
||||
#define SMB_MALLOC(cnt) new_array(char, cnt)
|
||||
#define SMB_MALLOC_P(obj) new_array(obj, 1)
|
||||
#define SMB_MALLOC_ARRAY(obj, cnt) new_array(obj, cnt)
|
||||
#define SMB_REALLOC(mem, cnt) realloc_array(mem, char, cnt)
|
||||
#define slprintf snprintf
|
||||
|
||||
int sys_acl_get_entry(SMB_ACL_T the_acl, int entry_id, SMB_ACL_ENTRY_T *entry_p);
|
||||
int sys_acl_get_tag_type(SMB_ACL_ENTRY_T entry_d, SMB_ACL_TAG_T *tag_type_p);
|
||||
int sys_acl_get_permset(SMB_ACL_ENTRY_T entry_d, SMB_ACL_PERMSET_T *permset_p);
|
||||
void *sys_acl_get_qualifier(SMB_ACL_ENTRY_T entry_d);
|
||||
SMB_ACL_T sys_acl_get_file(const char *path_p, SMB_ACL_TYPE_T type);
|
||||
SMB_ACL_T sys_acl_get_fd(int fd);
|
||||
int sys_acl_clear_perms(SMB_ACL_PERMSET_T permset);
|
||||
int sys_acl_add_perm(SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm);
|
||||
int sys_acl_get_perm(SMB_ACL_PERMSET_T permset, SMB_ACL_PERM_T perm);
|
||||
char *sys_acl_to_text(SMB_ACL_T the_acl, ssize_t *plen);
|
||||
SMB_ACL_T sys_acl_init(int count);
|
||||
int sys_acl_create_entry(SMB_ACL_T *pacl, SMB_ACL_ENTRY_T *pentry);
|
||||
int sys_acl_set_tag_type(SMB_ACL_ENTRY_T entry, SMB_ACL_TAG_T tagtype);
|
||||
int sys_acl_set_qualifier(SMB_ACL_ENTRY_T entry, void *qual);
|
||||
int sys_acl_set_permset(SMB_ACL_ENTRY_T entry, SMB_ACL_PERMSET_T permset);
|
||||
int sys_acl_valid(SMB_ACL_T theacl);
|
||||
int sys_acl_set_file(const char *name, SMB_ACL_TYPE_T acltype, SMB_ACL_T theacl);
|
||||
int sys_acl_set_fd(int fd, SMB_ACL_T theacl);
|
||||
int sys_acl_delete_def_file(const char *name);
|
||||
int sys_acl_free_text(char *text);
|
||||
int sys_acl_free_acl(SMB_ACL_T the_acl);
|
||||
int sys_acl_free_qualifier(void *qual, SMB_ACL_TAG_T tagtype);
|
||||
|
||||
#endif /* SUPPORT_ACLS */
|
||||
6
log.c
6
log.c
@@ -624,8 +624,10 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
|
||||
c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
|
||||
c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
|
||||
c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
|
||||
c[8] = '.';
|
||||
c[9] = '\0';
|
||||
c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.' : 'u';
|
||||
c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
|
||||
c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
|
||||
c[11] = '\0';
|
||||
|
||||
if (iflags & (ITEM_IS_NEW|ITEM_MISSING_DATA)) {
|
||||
char ch = iflags & ITEM_IS_NEW ? '+' : '?';
|
||||
|
||||
40
options.c
40
options.c
@@ -46,6 +46,7 @@ int copy_dirlinks = 0;
|
||||
int copy_links = 0;
|
||||
int preserve_links = 0;
|
||||
int preserve_hard_links = 0;
|
||||
int preserve_acls = 0;
|
||||
int preserve_perms = 0;
|
||||
int preserve_executability = 0;
|
||||
int preserve_devices = 0;
|
||||
@@ -198,6 +199,7 @@ static void print_rsync_version(enum logcode f)
|
||||
char const *got_socketpair = "no ";
|
||||
char const *have_inplace = "no ";
|
||||
char const *hardlinks = "no ";
|
||||
char const *acls = "no ";
|
||||
char const *links = "no ";
|
||||
char const *ipv6 = "no ";
|
||||
STRUCT_STAT *dumstat;
|
||||
@@ -214,6 +216,10 @@ static void print_rsync_version(enum logcode f)
|
||||
hardlinks = "";
|
||||
#endif
|
||||
|
||||
#ifdef SUPPORT_ACLS
|
||||
acls = "";
|
||||
#endif
|
||||
|
||||
#ifdef SUPPORT_LINKS
|
||||
links = "";
|
||||
#endif
|
||||
@@ -232,8 +238,8 @@ static void print_rsync_version(enum logcode f)
|
||||
(int)(sizeof (int64) * 8));
|
||||
rprintf(f, " %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
|
||||
got_socketpair, hardlinks, links, ipv6, have_inplace);
|
||||
rprintf(f, " %sappend\n",
|
||||
have_inplace);
|
||||
rprintf(f, " %sappend, %sACLs\n",
|
||||
have_inplace, acls);
|
||||
|
||||
#ifdef MAINTAINER_MODE
|
||||
rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
|
||||
@@ -279,7 +285,7 @@ void usage(enum logcode F)
|
||||
rprintf(F," -q, --quiet suppress non-error messages\n");
|
||||
rprintf(F," --no-motd suppress daemon-mode MOTD (see manpage caveat)\n");
|
||||
rprintf(F," -c, --checksum skip based on checksum, not mod-time & size\n");
|
||||
rprintf(F," -a, --archive archive mode; same as -rlptgoD (no -H)\n");
|
||||
rprintf(F," -a, --archive archive mode; same as -rlptgoD (no -H, -A)\n");
|
||||
rprintf(F," --no-OPTION turn off an implied OPTION (e.g. --no-D)\n");
|
||||
rprintf(F," -r, --recursive recurse into directories\n");
|
||||
rprintf(F," -R, --relative use relative path names\n");
|
||||
@@ -301,6 +307,9 @@ void usage(enum logcode F)
|
||||
rprintf(F," -p, --perms preserve permissions\n");
|
||||
rprintf(F," -E, --executability preserve the file's executability\n");
|
||||
rprintf(F," --chmod=CHMOD affect file and/or directory permissions\n");
|
||||
#ifdef SUPPORT_ACLS
|
||||
rprintf(F," -A, --acls preserve ACLs (implies --perms)\n");
|
||||
#endif
|
||||
rprintf(F," -o, --owner preserve owner (super-user only)\n");
|
||||
rprintf(F," -g, --group preserve group\n");
|
||||
rprintf(F," --devices preserve device files (super-user only)\n");
|
||||
@@ -421,6 +430,9 @@ static struct poptOption long_options[] = {
|
||||
{"no-perms", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 },
|
||||
{"no-p", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 },
|
||||
{"executability", 'E', POPT_ARG_NONE, &preserve_executability, 0, 0, 0 },
|
||||
{"acls", 'A', POPT_ARG_NONE, 0, 'A', 0, 0 },
|
||||
{"no-acls", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 },
|
||||
{"no-A", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 },
|
||||
{"times", 't', POPT_ARG_VAL, &preserve_times, 1, 0, 0 },
|
||||
{"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
|
||||
{"no-t", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
|
||||
@@ -1092,6 +1104,24 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
|
||||
usage(FINFO);
|
||||
exit_cleanup(0);
|
||||
|
||||
case 'A':
|
||||
#ifdef SUPPORT_ACLS
|
||||
preserve_acls = 1;
|
||||
preserve_perms = 1;
|
||||
break;
|
||||
#else
|
||||
/* FIXME: this should probably be ignored with a
|
||||
* warning and then countermeasures taken to
|
||||
* restrict group and other access in the presence
|
||||
* of any more restrictive ACLs, but this is safe
|
||||
* for now */
|
||||
snprintf(err_buf,sizeof(err_buf),
|
||||
"ACLs are not supported on this %s\n",
|
||||
am_server ? "server" : "client");
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
|
||||
default:
|
||||
/* A large opt value means that set_refuse_options()
|
||||
* turned this option off. */
|
||||
@@ -1551,6 +1581,10 @@ void server_options(char **args,int *argc)
|
||||
argstr[x++] = 'p';
|
||||
else if (preserve_executability && am_sender)
|
||||
argstr[x++] = 'E';
|
||||
#ifdef SUPPORT_ACLS
|
||||
if (preserve_acls)
|
||||
argstr[x++] = 'A';
|
||||
#endif
|
||||
if (recurse)
|
||||
argstr[x++] = 'r';
|
||||
if (always_checksum)
|
||||
|
||||
16
receiver.c
16
receiver.c
@@ -47,6 +47,7 @@ extern int keep_partial;
|
||||
extern int checksum_seed;
|
||||
extern int inplace;
|
||||
extern int delay_updates;
|
||||
extern mode_t orig_umask;
|
||||
extern struct stats stats;
|
||||
extern char *tmpdir;
|
||||
extern char *partial_dir;
|
||||
@@ -347,6 +348,10 @@ int recv_files(int f_in, char *local_name)
|
||||
int itemizing = am_server ? logfile_format_has_i : stdout_format_has_i;
|
||||
enum logcode log_code = log_before_transfer ? FLOG : FINFO;
|
||||
int max_phase = protocol_version >= 29 ? 2 : 1;
|
||||
int dflt_perms = (ACCESSPERMS & ~orig_umask);
|
||||
#ifdef SUPPORT_ACLS
|
||||
const char *parent_dirname = "";
|
||||
#endif
|
||||
int ndx, recv_ok;
|
||||
|
||||
if (verbose > 2)
|
||||
@@ -562,7 +567,16 @@ int recv_files(int f_in, char *local_name)
|
||||
* mode based on the local permissions and some heuristics. */
|
||||
if (!preserve_perms) {
|
||||
int exists = fd1 != -1;
|
||||
file->mode = dest_mode(file->mode, st.st_mode, exists);
|
||||
#ifdef SUPPORT_ACLS
|
||||
const char *dn = file->dirname ? file->dirname : ".";
|
||||
if (parent_dirname != dn
|
||||
&& strcmp(parent_dirname, dn) != 0) {
|
||||
dflt_perms = default_perms_for_dir(dn);
|
||||
parent_dirname = dn;
|
||||
}
|
||||
#endif
|
||||
file->mode = dest_mode(file->mode, st.st_mode,
|
||||
dflt_perms, exists);
|
||||
}
|
||||
|
||||
/* We now check to see if we are writing the file "inplace" */
|
||||
|
||||
81
rsync.c
81
rsync.c
@@ -31,6 +31,7 @@
|
||||
|
||||
extern int verbose;
|
||||
extern int dry_run;
|
||||
extern int preserve_acls;
|
||||
extern int preserve_perms;
|
||||
extern int preserve_executability;
|
||||
extern int preserve_times;
|
||||
@@ -49,7 +50,6 @@ extern int inplace;
|
||||
extern int flist_eof;
|
||||
extern int keep_dirlinks;
|
||||
extern int make_backups;
|
||||
extern mode_t orig_umask;
|
||||
extern struct file_list *cur_flist, *first_flist, *dir_flist;
|
||||
extern struct chmod_mode_struct *daemon_chmod_modes;
|
||||
|
||||
@@ -203,7 +203,8 @@ void free_sums(struct sum_struct *s)
|
||||
|
||||
/* This is only called when we aren't preserving permissions. Figure out what
|
||||
* the permissions should be and return them merged back into the mode. */
|
||||
mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int exists)
|
||||
mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int dflt_perms,
|
||||
int exists)
|
||||
{
|
||||
int new_mode;
|
||||
/* If the file already exists, we'll return the local permissions,
|
||||
@@ -220,56 +221,65 @@ mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int exists)
|
||||
new_mode |= (new_mode & 0444) >> 2;
|
||||
}
|
||||
} else {
|
||||
/* Apply the umask and turn off special permissions. */
|
||||
new_mode = flist_mode & (~CHMOD_BITS | (ACCESSPERMS & ~orig_umask));
|
||||
/* Apply destination default permissions and turn
|
||||
* off special permissions. */
|
||||
new_mode = flist_mode & (~CHMOD_BITS | dflt_perms);
|
||||
}
|
||||
return new_mode;
|
||||
}
|
||||
|
||||
int set_file_attrs(char *fname, struct file_struct *file, STRUCT_STAT *st,
|
||||
int set_file_attrs(char *fname, struct file_struct *file, statx *sxp,
|
||||
int flags)
|
||||
{
|
||||
int updated = 0;
|
||||
STRUCT_STAT st2;
|
||||
statx sx2;
|
||||
int change_uid, change_gid;
|
||||
mode_t new_mode = file->mode;
|
||||
|
||||
if (!st) {
|
||||
if (!sxp) {
|
||||
if (dry_run)
|
||||
return 1;
|
||||
if (link_stat(fname, &st2, 0) < 0) {
|
||||
if (link_stat(fname, &sx2.st, 0) < 0) {
|
||||
rsyserr(FERROR, errno, "stat %s failed",
|
||||
full_fname(fname));
|
||||
return 0;
|
||||
}
|
||||
st = &st2;
|
||||
#ifdef SUPPORT_ACLS
|
||||
sx2.acc_acl = sx2.def_acl = NULL;
|
||||
#endif
|
||||
if (!preserve_perms && S_ISDIR(new_mode)
|
||||
&& st->st_mode & S_ISGID) {
|
||||
&& sx2.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;
|
||||
}
|
||||
sxp = &sx2;
|
||||
}
|
||||
|
||||
if (!preserve_times || (S_ISDIR(st->st_mode) && omit_dir_times))
|
||||
#ifdef SUPPORT_ACLS
|
||||
if (preserve_acls && !S_ISLNK(file->mode) && !ACL_READY(*sxp))
|
||||
get_acl(fname, sxp);
|
||||
#endif
|
||||
|
||||
if (!preserve_times || (S_ISDIR(sxp->st.st_mode) && omit_dir_times))
|
||||
flags |= ATTRS_SKIP_MTIME;
|
||||
if (!(flags & ATTRS_SKIP_MTIME)
|
||||
&& cmp_time(st->st_mtime, file->modtime) != 0) {
|
||||
int ret = set_modtime(fname, file->modtime, st->st_mode);
|
||||
&& cmp_time(sxp->st.st_mtime, file->modtime) != 0) {
|
||||
int ret = set_modtime(fname, file->modtime, sxp->st.st_mode);
|
||||
if (ret < 0) {
|
||||
rsyserr(FERROR, errno, "failed to set times on %s",
|
||||
full_fname(fname));
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
if (ret == 0) /* ret == 1 if symlink could not be set */
|
||||
updated = 1;
|
||||
}
|
||||
|
||||
change_uid = am_root && preserve_uid && st->st_uid != F_UID(file);
|
||||
change_uid = am_root && preserve_uid && sxp->st.st_uid != F_UID(file);
|
||||
change_gid = preserve_gid && F_GID(file) != GID_NONE
|
||||
&& st->st_gid != F_GID(file);
|
||||
&& sxp->st.st_gid != F_GID(file);
|
||||
#if !defined HAVE_LCHOWN && !defined CHOWN_MODIFIES_SYMLINK
|
||||
if (S_ISLNK(st->st_mode))
|
||||
if (S_ISLNK(sxp->st.st_mode))
|
||||
;
|
||||
else
|
||||
#endif
|
||||
@@ -279,45 +289,57 @@ int set_file_attrs(char *fname, struct file_struct *file, STRUCT_STAT *st,
|
||||
rprintf(FINFO,
|
||||
"set uid of %s from %ld to %ld\n",
|
||||
fname,
|
||||
(long)st->st_uid, (long)F_UID(file));
|
||||
(long)sxp->st.st_uid, (long)F_UID(file));
|
||||
}
|
||||
if (change_gid) {
|
||||
rprintf(FINFO,
|
||||
"set gid of %s from %ld to %ld\n",
|
||||
fname,
|
||||
(long)st->st_gid, (long)F_GID(file));
|
||||
(long)sxp->st.st_gid, (long)F_GID(file));
|
||||
}
|
||||
}
|
||||
if (do_lchown(fname,
|
||||
change_uid ? F_UID(file) : st->st_uid,
|
||||
change_gid ? F_GID(file) : st->st_gid) != 0) {
|
||||
change_uid ? F_UID(file) : sxp->st.st_uid,
|
||||
change_gid ? F_GID(file) : sxp->st.st_gid) != 0) {
|
||||
/* shouldn't have attempted to change uid or gid
|
||||
* unless have the privilege */
|
||||
rsyserr(FERROR, errno, "%s %s failed",
|
||||
change_uid ? "chown" : "chgrp",
|
||||
full_fname(fname));
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
/* a lchown had been done - we have to re-stat if the
|
||||
* destination had the setuid or setgid bits set due
|
||||
* to the side effect of the chown call */
|
||||
if (st->st_mode & (S_ISUID | S_ISGID)) {
|
||||
link_stat(fname, st,
|
||||
keep_dirlinks && S_ISDIR(st->st_mode));
|
||||
if (sxp->st.st_mode & (S_ISUID | S_ISGID)) {
|
||||
link_stat(fname, &sxp->st,
|
||||
keep_dirlinks && S_ISDIR(sxp->st.st_mode));
|
||||
}
|
||||
updated = 1;
|
||||
}
|
||||
|
||||
if (daemon_chmod_modes && !S_ISLNK(new_mode))
|
||||
new_mode = tweak_mode(new_mode, daemon_chmod_modes);
|
||||
|
||||
#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) && set_acl(fname, file, sxp) == 0)
|
||||
updated = 1;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CHMOD
|
||||
if (!BITS_EQUAL(st->st_mode, new_mode, CHMOD_BITS)) {
|
||||
if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) {
|
||||
int ret = do_chmod(fname, new_mode);
|
||||
if (ret < 0) {
|
||||
rsyserr(FERROR, errno,
|
||||
"failed to set permissions on %s",
|
||||
full_fname(fname));
|
||||
return 0;
|
||||
goto cleanup;
|
||||
}
|
||||
if (ret == 0) /* ret == 1 if symlink could not be set */
|
||||
updated = 1;
|
||||
@@ -330,6 +352,11 @@ int set_file_attrs(char *fname, struct file_struct *file, STRUCT_STAT *st,
|
||||
else
|
||||
rprintf(FCLIENT, "%s is uptodate\n", fname);
|
||||
}
|
||||
cleanup:
|
||||
#ifdef SUPPORT_ACLS
|
||||
if (preserve_acls && sxp == &sx2)
|
||||
free_acl(&sx2);
|
||||
#endif
|
||||
return updated;
|
||||
}
|
||||
|
||||
|
||||
47
rsync.h
47
rsync.h
@@ -547,6 +547,10 @@ struct idev_node {
|
||||
#define IN_LOOPBACKNET 127
|
||||
#endif
|
||||
|
||||
#if HAVE_UNIXWARE_ACLS|HAVE_SOLARIS_ACLS|HAVE_HPUX_ACLS
|
||||
#define ACLS_NEED_MASK 1
|
||||
#endif
|
||||
|
||||
#define GID_NONE ((gid_t)-1)
|
||||
|
||||
union file_extras {
|
||||
@@ -566,6 +570,7 @@ struct file_struct {
|
||||
extern int file_extra_cnt;
|
||||
extern int preserve_uid;
|
||||
extern int preserve_gid;
|
||||
extern int preserve_acls;
|
||||
|
||||
#define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
|
||||
#define EXTRA_LEN (sizeof (union file_extras))
|
||||
@@ -598,10 +603,12 @@ extern int preserve_gid;
|
||||
/* When the associated option is on, all entries will have these present: */
|
||||
#define F_OWNER(f) REQ_EXTRA(f, preserve_uid)->unum
|
||||
#define F_GROUP(f) REQ_EXTRA(f, preserve_gid)->unum
|
||||
#define F_ACL(f) REQ_EXTRA(f, preserve_acls)->unum
|
||||
|
||||
/* These items are per-entry optional and mutally exclusive: */
|
||||
#define F_HL_GNUM(f) OPT_EXTRA(f, LEN64_BUMP(f))->num
|
||||
#define F_HL_PREV(f) OPT_EXTRA(f, LEN64_BUMP(f))->num
|
||||
#define F_DEF_ACL(f) OPT_EXTRA(f, LEN64_BUMP(f))->unum
|
||||
#define F_DIRDEV_P(f) (&OPT_EXTRA(f, LEN64_BUMP(f) + 2 - 1)->unum)
|
||||
#define F_DIRNODE_P(f) (&OPT_EXTRA(f, LEN64_BUMP(f) + 3 - 1)->num)
|
||||
|
||||
@@ -753,6 +760,17 @@ struct stats {
|
||||
|
||||
struct chmod_mode_struct;
|
||||
|
||||
#define EMPTY_ITEM_LIST {NULL, 0, 0}
|
||||
|
||||
typedef struct {
|
||||
void *items;
|
||||
size_t count;
|
||||
size_t malloced;
|
||||
} item_list;
|
||||
|
||||
#define EXPAND_ITEM_LIST(lp, type, incr) \
|
||||
(type*)expand_item_list(lp, sizeof (type), #type, incr)
|
||||
|
||||
#include "byteorder.h"
|
||||
#include "lib/mdfour.h"
|
||||
#include "lib/wildmatch.h"
|
||||
@@ -771,6 +789,16 @@ struct chmod_mode_struct;
|
||||
#define NORETURN __attribute__((__noreturn__))
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
STRUCT_STAT st;
|
||||
#ifdef SUPPORT_ACLS
|
||||
struct rsync_acl *acc_acl; /* access ACL */
|
||||
struct rsync_acl *def_acl; /* default ACL */
|
||||
#endif
|
||||
} statx;
|
||||
|
||||
#define ACL_READY(sx) ((sx).acc_acl != NULL)
|
||||
|
||||
#include "proto.h"
|
||||
|
||||
/* We have replacement versions of these if they're missing. */
|
||||
@@ -977,7 +1005,7 @@ size_t strlcat(char *d, const char *s, size_t bufsize);
|
||||
#define MY_GID() getgid()
|
||||
#endif
|
||||
|
||||
extern int verbose;
|
||||
extern int verbose, protocol_version;
|
||||
|
||||
#ifndef HAVE_INET_NTOP
|
||||
const char *inet_ntop(int af, const void *src, char *dst, size_t size);
|
||||
@@ -991,6 +1019,23 @@ int inet_pton(int af, const char *src, void *dst);
|
||||
const char *get_panic_action(void);
|
||||
#endif
|
||||
|
||||
static inline int32
|
||||
read_abbrevint30(int f)
|
||||
{
|
||||
if (protocol_version < 30)
|
||||
return read_int(f);
|
||||
return read_abbrevint(f);
|
||||
}
|
||||
|
||||
static inline void
|
||||
write_abbrevint30(int f, int32 x)
|
||||
{
|
||||
if (protocol_version < 30)
|
||||
write_int(f, x);
|
||||
else
|
||||
write_abbrevint(f, x);
|
||||
}
|
||||
|
||||
static inline int
|
||||
isDigit(const char *ptr)
|
||||
{
|
||||
|
||||
29
rsync.yo
29
rsync.yo
@@ -301,7 +301,7 @@ to the detailed description below for a complete description. verb(
|
||||
-q, --quiet suppress non-error messages
|
||||
--no-motd suppress daemon-mode MOTD (see caveat)
|
||||
-c, --checksum skip based on checksum, not mod-time & size
|
||||
-a, --archive archive mode; same as -rlptgoD (no -H)
|
||||
-a, --archive archive mode; same as -rlptgoD (no -H, -A)
|
||||
--no-OPTION turn off an implied OPTION (e.g. --no-D)
|
||||
-r, --recursive recurse into directories
|
||||
-R, --relative use relative path names
|
||||
@@ -323,6 +323,7 @@ to the detailed description below for a complete description. verb(
|
||||
-p, --perms preserve permissions
|
||||
-E, --executability preserve executability
|
||||
--chmod=CHMOD affect file and/or directory permissions
|
||||
-A, --acls preserve ACLs (implies -p)
|
||||
-o, --owner preserve owner (super-user only)
|
||||
-g, --group preserve group
|
||||
--devices preserve device files (super-user only)
|
||||
@@ -771,7 +772,9 @@ quote(itemization(
|
||||
permissions, though the bf(--executability) option might change just
|
||||
the execute permission for the file.
|
||||
it() New files get their "normal" permission bits set to the source
|
||||
file's permissions masked with the receiving end's umask setting, and
|
||||
file's permissions masked with the receiving directory's default
|
||||
permissions (either the receiving process's umask, or the permissions
|
||||
specified via the destination directory's default ACL), and
|
||||
their special permission bits disabled except in the case where a new
|
||||
directory inherits a setgid bit from its parent directory.
|
||||
))
|
||||
@@ -802,9 +805,11 @@ The preservation of the destination's setgid bit on newly-created
|
||||
directories when bf(--perms) is off was added in rsync 2.6.7. Older rsync
|
||||
versions erroneously preserved the three special permission bits for
|
||||
newly-created files when bf(--perms) was off, while overriding the
|
||||
destination's setgid bit setting on a newly-created directory. (Keep in
|
||||
mind that it is the version of the receiving rsync that affects this
|
||||
behavior.)
|
||||
destination's setgid bit setting on a newly-created directory. Default ACL
|
||||
observance was added to the ACL patch for rsync 2.6.7, so older (or
|
||||
non-ACL-enabled) rsyncs use the umask even if default ACLs are present.
|
||||
(Keep in mind that it is the version of the receiving rsync that affects
|
||||
these behaviors.)
|
||||
|
||||
dit(bf(-E, --executability)) This option causes rsync to preserve the
|
||||
executability (or non-executability) of regular files when bf(--perms) is
|
||||
@@ -822,6 +827,10 @@ quote(itemization(
|
||||
|
||||
If bf(--perms) is enabled, this option is ignored.
|
||||
|
||||
dit(bf(-A, --acls)) This option causes rsync to update the destination
|
||||
ACLs to be the same as the source ACLs. This nonstandard option only
|
||||
works if the remote rsync also supports it. bf(--acls) implies bf(--perms).
|
||||
|
||||
dit(bf(--chmod)) This option tells rsync to apply one or more
|
||||
comma-separated "chmod" strings to the permission of the files in the
|
||||
transfer. The resulting value is treated as though it was the permissions
|
||||
@@ -1432,8 +1441,8 @@ if the receiving rsync is at least version 2.6.7 (you can use bf(-vv)
|
||||
with older versions of rsync, but that also turns on the output of other
|
||||
verbose messages).
|
||||
|
||||
The "%i" escape has a cryptic output that is 9 letters long. The general
|
||||
format is like the string bf(YXcstpogz), where bf(Y) is replaced by the
|
||||
The "%i" escape has a cryptic output that is 11 letters long. The general
|
||||
format is like the string bf(YXcstpoguax), where bf(Y) is replaced by the
|
||||
type of update being done, bf(X) is replaced by the file-type, and the
|
||||
other letters represent attributes that may be output if they are being
|
||||
modified.
|
||||
@@ -1482,7 +1491,11 @@ quote(itemization(
|
||||
sender's value (requires bf(--owner) and super-user privileges).
|
||||
it() A bf(g) means the group is different and is being updated to the
|
||||
sender's value (requires bf(--group) and the authority to set the group).
|
||||
it() The bf(z) slot is reserved for future use.
|
||||
it() The bf(u) slot is reserved for reporting update (access) time changes
|
||||
(a feature that is not yet released).
|
||||
it() The bf(a) means that the ACL information changed.
|
||||
it() The bf(x) slot is reserved for reporting extended attribute changes
|
||||
(a feature that is not yet released).
|
||||
))
|
||||
|
||||
One other output is possible: when deleting files, the "%i" will output
|
||||
|
||||
281
smb_acls.h
Normal file
281
smb_acls.h
Normal file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
Unix SMB/Netbios implementation.
|
||||
Version 2.2.x
|
||||
Portable SMB ACL interface
|
||||
Copyright (C) Jeremy Allison 2000
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _SMB_ACLS_H
|
||||
#define _SMB_ACLS_H
|
||||
|
||||
#if defined HAVE_POSIX_ACLS
|
||||
|
||||
/* This is an identity mapping (just remove the SMB_). */
|
||||
|
||||
#define SMB_ACL_TAG_T acl_tag_t
|
||||
#define SMB_ACL_TYPE_T acl_type_t
|
||||
#define SMB_ACL_PERMSET_T acl_permset_t
|
||||
#define SMB_ACL_PERM_T acl_perm_t
|
||||
#define SMB_ACL_READ ACL_READ
|
||||
#define SMB_ACL_WRITE ACL_WRITE
|
||||
#define SMB_ACL_EXECUTE ACL_EXECUTE
|
||||
|
||||
/* Types of ACLs. */
|
||||
#define SMB_ACL_USER ACL_USER
|
||||
#define SMB_ACL_USER_OBJ ACL_USER_OBJ
|
||||
#define SMB_ACL_GROUP ACL_GROUP
|
||||
#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
|
||||
#define SMB_ACL_OTHER ACL_OTHER
|
||||
#define SMB_ACL_MASK ACL_MASK
|
||||
|
||||
#define SMB_ACL_T acl_t
|
||||
|
||||
#define SMB_ACL_ENTRY_T acl_entry_t
|
||||
|
||||
#define SMB_ACL_FIRST_ENTRY ACL_FIRST_ENTRY
|
||||
#define SMB_ACL_NEXT_ENTRY ACL_NEXT_ENTRY
|
||||
|
||||
#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
|
||||
#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
|
||||
|
||||
#elif defined HAVE_TRU64_ACLS
|
||||
|
||||
/* This is for DEC/Compaq Tru64 UNIX */
|
||||
|
||||
#define SMB_ACL_TAG_T acl_tag_t
|
||||
#define SMB_ACL_TYPE_T acl_type_t
|
||||
#define SMB_ACL_PERMSET_T acl_permset_t
|
||||
#define SMB_ACL_PERM_T acl_perm_t
|
||||
#define SMB_ACL_READ ACL_READ
|
||||
#define SMB_ACL_WRITE ACL_WRITE
|
||||
#define SMB_ACL_EXECUTE ACL_EXECUTE
|
||||
|
||||
/* Types of ACLs. */
|
||||
#define SMB_ACL_USER ACL_USER
|
||||
#define SMB_ACL_USER_OBJ ACL_USER_OBJ
|
||||
#define SMB_ACL_GROUP ACL_GROUP
|
||||
#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
|
||||
#define SMB_ACL_OTHER ACL_OTHER
|
||||
#define SMB_ACL_MASK ACL_MASK
|
||||
|
||||
#define SMB_ACL_T acl_t
|
||||
|
||||
#define SMB_ACL_ENTRY_T acl_entry_t
|
||||
|
||||
#define SMB_ACL_FIRST_ENTRY 0
|
||||
#define SMB_ACL_NEXT_ENTRY 1
|
||||
|
||||
#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
|
||||
#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
|
||||
|
||||
#elif defined HAVE_UNIXWARE_ACLS || defined HAVE_SOLARIS_ACLS
|
||||
/*
|
||||
* Donated by Michael Davidson <md@sco.COM> for UnixWare / OpenUNIX.
|
||||
* Modified by Toomas Soome <tsoome@ut.ee> for Solaris.
|
||||
*/
|
||||
|
||||
/* SVR4.2 ES/MP ACLs */
|
||||
typedef int SMB_ACL_TAG_T;
|
||||
typedef int SMB_ACL_TYPE_T;
|
||||
typedef ushort *SMB_ACL_PERMSET_T;
|
||||
typedef ushort SMB_ACL_PERM_T;
|
||||
#define SMB_ACL_READ 4
|
||||
#define SMB_ACL_WRITE 2
|
||||
#define SMB_ACL_EXECUTE 1
|
||||
|
||||
/* Types of ACLs. */
|
||||
#define SMB_ACL_USER USER
|
||||
#define SMB_ACL_USER_OBJ USER_OBJ
|
||||
#define SMB_ACL_GROUP GROUP
|
||||
#define SMB_ACL_GROUP_OBJ GROUP_OBJ
|
||||
#define SMB_ACL_OTHER OTHER_OBJ
|
||||
#define SMB_ACL_MASK CLASS_OBJ
|
||||
|
||||
typedef struct SMB_ACL_T {
|
||||
int size;
|
||||
int count;
|
||||
int next;
|
||||
struct acl acl[1];
|
||||
} *SMB_ACL_T;
|
||||
|
||||
typedef struct acl *SMB_ACL_ENTRY_T;
|
||||
|
||||
#define SMB_ACL_FIRST_ENTRY 0
|
||||
#define SMB_ACL_NEXT_ENTRY 1
|
||||
|
||||
#define SMB_ACL_TYPE_ACCESS 0
|
||||
#define SMB_ACL_TYPE_DEFAULT 1
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
#define SMB_ACL_LOSES_SPECIAL_MODE_BITS
|
||||
#endif
|
||||
|
||||
#elif defined HAVE_HPUX_ACLS
|
||||
|
||||
/*
|
||||
* Based on the Solaris & UnixWare code.
|
||||
*/
|
||||
|
||||
#undef GROUP
|
||||
#include <sys/aclv.h>
|
||||
|
||||
/* SVR4.2 ES/MP ACLs */
|
||||
typedef int SMB_ACL_TAG_T;
|
||||
typedef int SMB_ACL_TYPE_T;
|
||||
typedef ushort *SMB_ACL_PERMSET_T;
|
||||
typedef ushort SMB_ACL_PERM_T;
|
||||
#define SMB_ACL_READ 4
|
||||
#define SMB_ACL_WRITE 2
|
||||
#define SMB_ACL_EXECUTE 1
|
||||
|
||||
/* Types of ACLs. */
|
||||
#define SMB_ACL_USER USER
|
||||
#define SMB_ACL_USER_OBJ USER_OBJ
|
||||
#define SMB_ACL_GROUP GROUP
|
||||
#define SMB_ACL_GROUP_OBJ GROUP_OBJ
|
||||
#define SMB_ACL_OTHER OTHER_OBJ
|
||||
#define SMB_ACL_MASK CLASS_OBJ
|
||||
|
||||
typedef struct SMB_ACL_T {
|
||||
int size;
|
||||
int count;
|
||||
int next;
|
||||
struct acl acl[1];
|
||||
} *SMB_ACL_T;
|
||||
|
||||
typedef struct acl *SMB_ACL_ENTRY_T;
|
||||
|
||||
#define SMB_ACL_FIRST_ENTRY 0
|
||||
#define SMB_ACL_NEXT_ENTRY 1
|
||||
|
||||
#define SMB_ACL_TYPE_ACCESS 0
|
||||
#define SMB_ACL_TYPE_DEFAULT 1
|
||||
|
||||
#elif defined HAVE_IRIX_ACLS
|
||||
|
||||
#define SMB_ACL_TAG_T acl_tag_t
|
||||
#define SMB_ACL_TYPE_T acl_type_t
|
||||
#define SMB_ACL_PERMSET_T acl_permset_t
|
||||
#define SMB_ACL_PERM_T acl_perm_t
|
||||
#define SMB_ACL_READ ACL_READ
|
||||
#define SMB_ACL_WRITE ACL_WRITE
|
||||
#define SMB_ACL_EXECUTE ACL_EXECUTE
|
||||
|
||||
/* Types of ACLs. */
|
||||
#define SMB_ACL_USER ACL_USER
|
||||
#define SMB_ACL_USER_OBJ ACL_USER_OBJ
|
||||
#define SMB_ACL_GROUP ACL_GROUP
|
||||
#define SMB_ACL_GROUP_OBJ ACL_GROUP_OBJ
|
||||
#define SMB_ACL_OTHER ACL_OTHER_OBJ
|
||||
#define SMB_ACL_MASK ACL_MASK
|
||||
|
||||
typedef struct SMB_ACL_T {
|
||||
int next;
|
||||
BOOL freeaclp;
|
||||
struct acl *aclp;
|
||||
} *SMB_ACL_T;
|
||||
|
||||
#define SMB_ACL_ENTRY_T acl_entry_t
|
||||
|
||||
#define SMB_ACL_FIRST_ENTRY 0
|
||||
#define SMB_ACL_NEXT_ENTRY 1
|
||||
|
||||
#define SMB_ACL_TYPE_ACCESS ACL_TYPE_ACCESS
|
||||
#define SMB_ACL_TYPE_DEFAULT ACL_TYPE_DEFAULT
|
||||
|
||||
#elif defined HAVE_AIX_ACLS
|
||||
|
||||
/* Donated by Medha Date, mdate@austin.ibm.com, for IBM */
|
||||
|
||||
#include "/usr/include/acl.h"
|
||||
|
||||
typedef uint *SMB_ACL_PERMSET_T;
|
||||
|
||||
struct acl_entry_link{
|
||||
struct acl_entry_link *prevp;
|
||||
struct new_acl_entry *entryp;
|
||||
struct acl_entry_link *nextp;
|
||||
int count;
|
||||
};
|
||||
|
||||
struct new_acl_entry{
|
||||
unsigned short ace_len;
|
||||
unsigned short ace_type;
|
||||
unsigned int ace_access;
|
||||
struct ace_id ace_id[1];
|
||||
};
|
||||
|
||||
#define SMB_ACL_ENTRY_T struct new_acl_entry*
|
||||
#define SMB_ACL_T struct acl_entry_link*
|
||||
|
||||
#define SMB_ACL_TAG_T unsigned short
|
||||
#define SMB_ACL_TYPE_T int
|
||||
#define SMB_ACL_PERM_T uint
|
||||
#define SMB_ACL_READ S_IRUSR
|
||||
#define SMB_ACL_WRITE S_IWUSR
|
||||
#define SMB_ACL_EXECUTE S_IXUSR
|
||||
|
||||
/* Types of ACLs. */
|
||||
#define SMB_ACL_USER ACEID_USER
|
||||
#define SMB_ACL_USER_OBJ 3
|
||||
#define SMB_ACL_GROUP ACEID_GROUP
|
||||
#define SMB_ACL_GROUP_OBJ 4
|
||||
#define SMB_ACL_OTHER 5
|
||||
#define SMB_ACL_MASK 6
|
||||
|
||||
|
||||
#define SMB_ACL_FIRST_ENTRY 1
|
||||
#define SMB_ACL_NEXT_ENTRY 2
|
||||
|
||||
#define SMB_ACL_TYPE_ACCESS 0
|
||||
#define SMB_ACL_TYPE_DEFAULT 1
|
||||
|
||||
#else /* No ACLs. */
|
||||
|
||||
/* No ACLS - fake it. */
|
||||
#define SMB_ACL_TAG_T int
|
||||
#define SMB_ACL_TYPE_T int
|
||||
#define SMB_ACL_PERMSET_T mode_t
|
||||
#define SMB_ACL_PERM_T mode_t
|
||||
#define SMB_ACL_READ S_IRUSR
|
||||
#define SMB_ACL_WRITE S_IWUSR
|
||||
#define SMB_ACL_EXECUTE S_IXUSR
|
||||
|
||||
/* Types of ACLs. */
|
||||
#define SMB_ACL_USER 0
|
||||
#define SMB_ACL_USER_OBJ 1
|
||||
#define SMB_ACL_GROUP 2
|
||||
#define SMB_ACL_GROUP_OBJ 3
|
||||
#define SMB_ACL_OTHER 4
|
||||
#define SMB_ACL_MASK 5
|
||||
|
||||
typedef struct SMB_ACL_T {
|
||||
int dummy;
|
||||
} *SMB_ACL_T;
|
||||
|
||||
typedef struct SMB_ACL_ENTRY_T {
|
||||
int dummy;
|
||||
} *SMB_ACL_ENTRY_T;
|
||||
|
||||
#define SMB_ACL_FIRST_ENTRY 0
|
||||
#define SMB_ACL_NEXT_ENTRY 1
|
||||
|
||||
#define SMB_ACL_TYPE_ACCESS 0
|
||||
#define SMB_ACL_TYPE_DEFAULT 1
|
||||
|
||||
#endif /* No ACLs. */
|
||||
#endif /* _SMB_ACLS_H */
|
||||
39
testsuite/acls.test
Normal file
39
testsuite/acls.test
Normal file
@@ -0,0 +1,39 @@
|
||||
#! /bin/sh
|
||||
|
||||
# This program is distributable under the terms of the GNU GPL (see
|
||||
# COPYING).
|
||||
|
||||
# Test that rsync handles basic ACL preservation.
|
||||
|
||||
. $srcdir/testsuite/rsync.fns
|
||||
|
||||
$RSYNC --version | grep ", ACLs" >/dev/null || test_skipped "Rsync is configured without ACL support"
|
||||
|
||||
case "$RSYNC" in
|
||||
*protocol=29*) test_skipped "ACLs require protocol 30" ;;
|
||||
esac
|
||||
|
||||
case "$setfacl_nodef" in
|
||||
true) test_skipped "I don't know how to use your setfacl command" ;;
|
||||
esac
|
||||
|
||||
makepath "$fromdir/foo"
|
||||
echo something >"$fromdir/file1"
|
||||
echo else >"$fromdir/file2"
|
||||
|
||||
files='foo file1 file2'
|
||||
|
||||
setfacl -m u:0:7 "$fromdir/foo" || test_skipped "Your filesystem has ACLs disabled"
|
||||
setfacl -m u:0:5 "$fromdir/file1"
|
||||
setfacl -m u:0:5 "$fromdir/file2"
|
||||
|
||||
$RSYNC -avvA "$fromdir/" "$todir/"
|
||||
|
||||
cd "$fromdir"
|
||||
getfacl $files >"$scratchdir/acls.txt"
|
||||
|
||||
cd "$todir"
|
||||
getfacl $files | diff $diffopt "$scratchdir/acls.txt" -
|
||||
|
||||
# The script would have aborted on error, so getting here means we've won.
|
||||
exit 0
|
||||
70
testsuite/default-acls.test
Normal file
70
testsuite/default-acls.test
Normal file
@@ -0,0 +1,70 @@
|
||||
#! /bin/sh
|
||||
|
||||
# This program is distributable under the terms of the GNU GPL (see
|
||||
# COPYING).
|
||||
|
||||
# Test that rsync obeys default ACLs. -- Matt McCutchen
|
||||
|
||||
. $srcdir/testsuite/rsync.fns
|
||||
|
||||
$RSYNC --version | grep ", ACLs" >/dev/null || test_skipped "Rsync is configured without ACL support"
|
||||
|
||||
case "$RSYNC" in
|
||||
*protocol=29*) test_skipped "ACLs require protocol 30" ;;
|
||||
esac
|
||||
|
||||
case "$setfacl_nodef" in
|
||||
true) test_skipped "I don't know how to use your setfacl command" ;;
|
||||
*-k*) opts='-dm u::7,g::5,o:5' ;;
|
||||
*) opts='-m d:u::7,d:g::5,d:o:5' ;;
|
||||
esac
|
||||
setfacl $opts "$scratchdir" || test_skipped "Your filesystem has ACLs disabled"
|
||||
|
||||
# Call as: testit <dirname> <default-acl> <file-expected> <program-expected>
|
||||
testit() {
|
||||
todir="$scratchdir/$1"
|
||||
mkdir "$todir"
|
||||
$setfacl_nodef "$todir"
|
||||
if [ "$2" ]; then
|
||||
case "$setfacl_nodef" in
|
||||
*-k*) opts="-dm $2" ;;
|
||||
*) opts="-m `echo $2 | sed 's/\([ugom]:\)/d:\1/g'`"
|
||||
esac
|
||||
setfacl $opts "$todir"
|
||||
fi
|
||||
# Make sure we obey ACLs when creating a directory to hold multiple transferred files,
|
||||
# even though the directory itself is outside the transfer
|
||||
$RSYNC -rvv "$scratchdir/dir" "$scratchdir/file" "$scratchdir/program" "$todir/to/"
|
||||
check_perms "$todir/to" $4 "Target $1"
|
||||
check_perms "$todir/to/dir" $4 "Target $1"
|
||||
check_perms "$todir/to/file" $3 "Target $1"
|
||||
check_perms "$todir/to/program" $4 "Target $1"
|
||||
# Make sure get_local_name doesn't mess us up when transferring only one file
|
||||
$RSYNC -rvv "$scratchdir/file" "$todir/to/anotherfile"
|
||||
check_perms "$todir/to/anotherfile" $3 "Target $1"
|
||||
# Make sure we obey default ACLs when not transferring a regular file
|
||||
$RSYNC -rvv "$scratchdir/dir/" "$todir/to/anotherdir/"
|
||||
check_perms "$todir/to/anotherdir" $4 "Target $1"
|
||||
}
|
||||
|
||||
mkdir "$scratchdir/dir"
|
||||
echo "File!" >"$scratchdir/file"
|
||||
echo "#!/bin/sh" >"$scratchdir/program"
|
||||
chmod 777 "$scratchdir/dir"
|
||||
chmod 666 "$scratchdir/file"
|
||||
chmod 777 "$scratchdir/program"
|
||||
|
||||
# Test some target directories
|
||||
umask 0077
|
||||
testit da777 u::7,g::7,o:7 rw-rw-rw- rwxrwxrwx
|
||||
testit da775 u::7,g::7,o:5 rw-rw-r-- rwxrwxr-x
|
||||
testit da750 u::7,g::5,o:0 rw-r----- rwxr-x---
|
||||
testit da770mask u::7,u:0:7,g::0,m:7,o:0 rw-rw---- rwxrwx---
|
||||
testit noda1 '' rw------- rwx------
|
||||
umask 0000
|
||||
testit noda2 '' rw-rw-rw- rwxrwxrwx
|
||||
umask 0022
|
||||
testit noda3 '' rw-r--r-- rwxr-xr-x
|
||||
|
||||
# Hooray
|
||||
exit 0
|
||||
@@ -42,14 +42,14 @@ touch -r "$fromdir/block" "$fromdir/block2"
|
||||
$RSYNC -ai "$fromdir/block" "$todir/block2" \
|
||||
| tee "$outfile"
|
||||
cat <<EOT >"$chkfile"
|
||||
cD+++++++ block
|
||||
cD+++++++++ block
|
||||
EOT
|
||||
diff $diffopt "$chkfile" "$outfile" || test_fail "test 1 failed"
|
||||
|
||||
$RSYNC -ai "$fromdir/block2" "$todir/block" \
|
||||
| tee "$outfile"
|
||||
cat <<EOT >"$chkfile"
|
||||
cD+++++++ block2
|
||||
cD+++++++++ block2
|
||||
EOT
|
||||
diff $diffopt "$chkfile" "$outfile" || test_fail "test 2 failed"
|
||||
|
||||
@@ -58,7 +58,7 @@ sleep 1
|
||||
$RSYNC -Di "$fromdir/block3" "$todir/block" \
|
||||
| tee "$outfile"
|
||||
cat <<EOT >"$chkfile"
|
||||
cD..T.... block3
|
||||
cD..T...... block3
|
||||
EOT
|
||||
diff $diffopt "$chkfile" "$outfile" || test_fail "test 3 failed"
|
||||
|
||||
@@ -66,15 +66,15 @@ $RSYNC -aiHvv "$fromdir/" "$todir/" \
|
||||
| tee "$outfile"
|
||||
filter_outfile
|
||||
cat <<EOT >"$chkfile"
|
||||
.d..t.... ./
|
||||
cD..t.... block
|
||||
cD block2
|
||||
cD+++++++ block3
|
||||
hD+++++++ block2.5 => block3
|
||||
cD+++++++ char
|
||||
cD+++++++ char2
|
||||
cD+++++++ char3
|
||||
cS+++++++ fifo
|
||||
.d..t...... ./
|
||||
cD..t...... block
|
||||
cD block2
|
||||
cD+++++++++ block3
|
||||
hD+++++++++ block2.5 => block3
|
||||
cD+++++++++ char
|
||||
cD+++++++++ char2
|
||||
cD+++++++++ char3
|
||||
cS+++++++++ fifo
|
||||
EOT
|
||||
if test ! -b "$fromdir/block2.5"; then
|
||||
sed -e '/block2\.5/d' \
|
||||
@@ -94,15 +94,15 @@ if test -b "$fromdir/block2.5"; then
|
||||
$RSYNC -aii --link-dest="$todir" "$fromdir/" "$chkdir/" \
|
||||
| tee "$outfile"
|
||||
cat <<EOT >"$chkfile"
|
||||
cd ./
|
||||
hD block
|
||||
hD block2
|
||||
hD block2.5
|
||||
hD block3
|
||||
hD char
|
||||
hD char2
|
||||
hD char3
|
||||
hS fifo
|
||||
cd ./
|
||||
hD block
|
||||
hD block2
|
||||
hD block2.5
|
||||
hD block3
|
||||
hD char
|
||||
hD char2
|
||||
hD char3
|
||||
hS fifo
|
||||
EOT
|
||||
diff $diffopt "$chkfile" "$outfile" || test_fail "test 4 failed"
|
||||
fi
|
||||
|
||||
@@ -47,16 +47,16 @@ rm -f "$to2dir" "$to2dir.test"
|
||||
$RSYNC -iplr "$fromdir/" "$todir/" \
|
||||
| tee "$outfile"
|
||||
sed -e "$sed_cmd" <<EOT >"$chkfile"
|
||||
cd+++++++ ./
|
||||
cd+++++++ bar/
|
||||
cd+++++++ foo/_P30_
|
||||
cd+++++++ bar/baz/
|
||||
>f+++++++ bar/baz/rsync
|
||||
cd+++++++ foo/_P29_
|
||||
>f+++++++ foo/config1
|
||||
>f+++++++ foo/config2
|
||||
>f+++++++ foo/extra
|
||||
cL+++++++ foo/sym -> ../bar/baz/rsync
|
||||
cd+++++++++ ./
|
||||
cd+++++++++ bar/
|
||||
cd+++++++++ foo/_P30_
|
||||
cd+++++++++ bar/baz/
|
||||
>f+++++++++ bar/baz/rsync
|
||||
cd+++++++++ foo/_P29_
|
||||
>f+++++++++ foo/config1
|
||||
>f+++++++++ foo/config2
|
||||
>f+++++++++ foo/extra
|
||||
cL+++++++++ foo/sym -> ../bar/baz/rsync
|
||||
EOT
|
||||
diff $diffopt "$chkfile" "$outfile" || test_fail "test 1 failed"
|
||||
|
||||
@@ -68,10 +68,10 @@ chmod 601 "$fromdir/foo/config2"
|
||||
$RSYNC -iplrH "$fromdir/" "$todir/" \
|
||||
| tee "$outfile"
|
||||
sed -e "$sed_cmd" <<EOT >"$chkfile"
|
||||
>f..T.... bar/baz/rsync
|
||||
>f..T.... foo/config1
|
||||
>f.sTp... foo/config2
|
||||
hf..T.... foo/extra => foo/config1
|
||||
>f..T...... bar/baz/rsync
|
||||
>f..T...... foo/config1
|
||||
>f.sTp..... foo/config2
|
||||
hf..T...... foo/extra => foo/config1
|
||||
EOT
|
||||
diff $diffopt "$chkfile" "$outfile" || test_fail "test 2 failed"
|
||||
|
||||
@@ -88,12 +88,12 @@ chmod 777 "$todir/bar/baz/rsync"
|
||||
$RSYNC -iplrtc "$fromdir/" "$todir/" \
|
||||
| tee "$outfile"
|
||||
sed -e "$sed_cmd" <<EOT >"$chkfile"
|
||||
.d..t.... foo/_P30_
|
||||
.f..tp... bar/baz/rsync
|
||||
.d..t.... foo/_P29_
|
||||
.f..t.... foo/config1
|
||||
>fcstp... foo/config2
|
||||
cL..T.... foo/sym -> ../bar/baz/rsync
|
||||
.d..t...... foo/_P30_
|
||||
.f..tp..... bar/baz/rsync
|
||||
.d..t...... foo/_P29_
|
||||
.f..t...... foo/config1
|
||||
>fcstp..... foo/config2
|
||||
cL..T...... foo/sym -> ../bar/baz/rsync
|
||||
EOT
|
||||
diff $diffopt "$chkfile" "$outfile" || test_fail "test 3 failed"
|
||||
|
||||
@@ -118,15 +118,15 @@ $RSYNC -ivvplrtH "$fromdir/" "$todir/" \
|
||||
| tee "$outfile"
|
||||
filter_outfile
|
||||
sed -e "$sed_cmd" <<EOT >"$chkfile"
|
||||
.d ./
|
||||
.d bar/
|
||||
.d bar/baz/
|
||||
.f...p... bar/baz/rsync
|
||||
.d foo/
|
||||
.f foo/config1
|
||||
>f..t.... foo/config2
|
||||
hf foo/extra
|
||||
.L foo/sym -> ../bar/baz/rsync
|
||||
.d ./
|
||||
.d bar/
|
||||
.d bar/baz/
|
||||
.f...p..... bar/baz/rsync
|
||||
.d foo/
|
||||
.f foo/config1
|
||||
>f..t...... foo/config2
|
||||
hf foo/extra
|
||||
.L foo/sym -> ../bar/baz/rsync
|
||||
EOT
|
||||
diff $diffopt "$chkfile" "$outfile" || test_fail "test 5 failed"
|
||||
|
||||
@@ -145,8 +145,8 @@ touch "$todir/foo/config2"
|
||||
$RSYNC -iplrtH "$fromdir/" "$todir/" \
|
||||
| tee "$outfile"
|
||||
sed -e "$sed_cmd" <<EOT >"$chkfile"
|
||||
.f...p... foo/config1
|
||||
>f..t.... foo/config2
|
||||
.f...p..... foo/config1
|
||||
>f..t...... foo/config2
|
||||
EOT
|
||||
diff $diffopt "$chkfile" "$outfile" || test_fail "test 7 failed"
|
||||
|
||||
@@ -154,15 +154,15 @@ $RSYNC -ivvplrtH --copy-dest=../to "$fromdir/" "$to2dir/" \
|
||||
| tee "$outfile"
|
||||
filter_outfile
|
||||
sed -e "$sed_cmd" <<EOT >"$chkfile"
|
||||
cd ./
|
||||
cd bar/
|
||||
cd bar/baz/
|
||||
cf bar/baz/rsync
|
||||
cd foo/
|
||||
cf foo/config1
|
||||
cf foo/config2
|
||||
hf foo/extra => foo/config1
|
||||
cL foo/sym -> ../bar/baz/rsync
|
||||
cd ./
|
||||
cd bar/
|
||||
cd bar/baz/
|
||||
cf bar/baz/rsync
|
||||
cd foo/
|
||||
cf foo/config1
|
||||
cf foo/config2
|
||||
hf foo/extra => foo/config1
|
||||
cL foo/sym -> ../bar/baz/rsync
|
||||
EOT
|
||||
diff $diffopt "$chkfile" "$outfile" || test_fail "test 8 failed"
|
||||
|
||||
@@ -170,7 +170,7 @@ rm -rf "$to2dir"
|
||||
$RSYNC -iplrtH --copy-dest=../to "$fromdir/" "$to2dir/" \
|
||||
| tee "$outfile"
|
||||
sed -e "$sed_cmd" <<EOT >"$chkfile"
|
||||
hf foo/extra => foo/config1
|
||||
hf foo/extra => foo/config1
|
||||
EOT
|
||||
diff $diffopt "$chkfile" "$outfile" || test_fail "test 9 failed"
|
||||
|
||||
@@ -196,15 +196,15 @@ $RSYNC -ivvplrtH --link-dest="$todir" "$fromdir/" "$to2dir/" \
|
||||
| tee "$outfile"
|
||||
filter_outfile
|
||||
sed -e "$sed_cmd" <<EOT >"$chkfile"
|
||||
cd ./
|
||||
cd bar/
|
||||
cd bar/baz/
|
||||
hf bar/baz/rsync
|
||||
cd foo/
|
||||
hf foo/config1
|
||||
hf foo/config2
|
||||
hf foo/extra => foo/config1
|
||||
$L foo/sym -> ../bar/baz/rsync
|
||||
cd ./
|
||||
cd bar/
|
||||
cd bar/baz/
|
||||
hf bar/baz/rsync
|
||||
cd foo/
|
||||
hf foo/config1
|
||||
hf foo/config2
|
||||
hf foo/extra => foo/config1
|
||||
$L foo/sym -> ../bar/baz/rsync
|
||||
EOT
|
||||
diff $diffopt "$chkfile" "$outfile" || test_fail "test 11 failed"
|
||||
|
||||
@@ -244,15 +244,15 @@ $RSYNC -ivvplrtH --compare-dest="$todir" "$fromdir/" "$to2dir/" \
|
||||
| tee "$outfile"
|
||||
filter_outfile
|
||||
sed -e "$sed_cmd" <<EOT >"$chkfile"
|
||||
cd ./
|
||||
cd bar/
|
||||
cd bar/baz/
|
||||
.f bar/baz/rsync
|
||||
cd foo/
|
||||
.f foo/config1
|
||||
.f foo/config2
|
||||
.f foo/extra
|
||||
.L foo/sym -> ../bar/baz/rsync
|
||||
cd ./
|
||||
cd bar/
|
||||
cd bar/baz/
|
||||
.f bar/baz/rsync
|
||||
cd foo/
|
||||
.f foo/config1
|
||||
.f foo/config2
|
||||
.f foo/extra
|
||||
.L foo/sym -> ../bar/baz/rsync
|
||||
EOT
|
||||
diff $diffopt "$chkfile" "$outfile" || test_fail "test 15 failed"
|
||||
|
||||
|
||||
19
uidlist.c
19
uidlist.c
@@ -35,6 +35,7 @@ extern int verbose;
|
||||
extern int am_root;
|
||||
extern int preserve_uid;
|
||||
extern int preserve_gid;
|
||||
extern int preserve_acls;
|
||||
extern int numeric_ids;
|
||||
|
||||
struct idlist {
|
||||
@@ -271,7 +272,7 @@ void send_uid_list(int f)
|
||||
{
|
||||
struct idlist *list;
|
||||
|
||||
if (preserve_uid) {
|
||||
if (preserve_uid || preserve_acls) {
|
||||
int len;
|
||||
/* we send sequences of uid/byte-length/name */
|
||||
for (list = uidlist; list; list = list->next) {
|
||||
@@ -288,7 +289,7 @@ void send_uid_list(int f)
|
||||
write_int(f, 0);
|
||||
}
|
||||
|
||||
if (preserve_gid) {
|
||||
if (preserve_gid || preserve_acls) {
|
||||
int len;
|
||||
for (list = gidlist; list; list = list->next) {
|
||||
if (!list->name)
|
||||
@@ -328,18 +329,28 @@ void recv_uid_list(int f, struct file_list *flist)
|
||||
{
|
||||
int id, i;
|
||||
|
||||
if (preserve_uid && !numeric_ids) {
|
||||
if ((preserve_uid || preserve_acls) && !numeric_ids) {
|
||||
/* read the uid list */
|
||||
while ((id = read_int(f)) != 0)
|
||||
recv_user_name(f, (uid_t)id);
|
||||
}
|
||||
|
||||
if (preserve_gid && !numeric_ids) {
|
||||
if ((preserve_gid || preserve_acls) && !numeric_ids) {
|
||||
/* read the gid list */
|
||||
while ((id = read_int(f)) != 0)
|
||||
recv_group_name(f, (gid_t)id);
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_ACLS
|
||||
if (preserve_acls && !numeric_ids) {
|
||||
id_t *id;
|
||||
while ((id = next_acl_uid(flist)) != NULL)
|
||||
*id = match_uid(*id);
|
||||
while ((id = next_acl_gid(flist)) != NULL)
|
||||
*id = match_gid(*id);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Now convert all the uids/gids from sender values to our values. */
|
||||
if (am_root && preserve_uid && !numeric_ids) {
|
||||
for (i = 0; i < flist->count; i++)
|
||||
|
||||
28
util.c
28
util.c
@@ -1466,3 +1466,31 @@ int bitbag_next_bit(struct bitbag *bb, int after)
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void *expand_item_list(item_list *lp, size_t item_size,
|
||||
const char *desc, int incr)
|
||||
{
|
||||
/* First time through, 0 <= 0, so list is expanded. */
|
||||
if (lp->malloced <= lp->count) {
|
||||
void *new_ptr;
|
||||
size_t new_size = lp->malloced;
|
||||
if (incr < 0)
|
||||
new_size -= incr; /* increase slowly */
|
||||
else if (new_size < (size_t)incr)
|
||||
new_size += incr;
|
||||
else
|
||||
new_size *= 2;
|
||||
new_ptr = realloc_array(lp->items, char, new_size * item_size);
|
||||
if (verbose >= 4) {
|
||||
rprintf(FINFO, "[%s] expand %s to %.0f bytes, did%s move\n",
|
||||
who_am_i(), desc, (double)new_size * item_size,
|
||||
new_ptr == lp->items ? " not" : "");
|
||||
}
|
||||
if (!new_ptr)
|
||||
out_of_memory("expand_item_list");
|
||||
|
||||
lp->items = new_ptr;
|
||||
lp->malloced = new_size;
|
||||
}
|
||||
return (char*)lp->items + (lp->count++ * item_size);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user