From 8d921300191568f32c37697dc19bc398443d876b Mon Sep 17 00:00:00 2001 From: Chris Dunlop Date: Tue, 7 Sep 2010 14:10:05 +1000 Subject: [PATCH] fake-super: give owner full permissions on created files and directories Signed-off-by: Chris Dunlop --- generator.c | 27 +++++- receiver.c | 17 +++- testsuite/fake-super.test | 222 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 262 insertions(+), 4 deletions(-) create mode 100644 testsuite/fake-super.test diff --git a/generator.c b/generator.c index be36e63..81e2b0b 100644 --- a/generator.c +++ b/generator.c @@ -1094,6 +1094,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, int is_dir = !S_ISDIR(file->mode) ? 0 : inc_recurse && ndx != cur_flist->ndx_start - 1 ? -1 : 1; + uint16 initial_mode; if (DEBUG_GTE(GENR, 1)) rprintf(FINFO, "recv_generator(%s,%d)\n", fname, ndx); @@ -1237,7 +1238,21 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, && (S_ISDIR(sx.st.st_mode) || delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_DIR) != 0)) goto cleanup; /* Any errors get reported later. */ - if (do_mkdir(fname, file->mode & 0700) == 0) + if (am_root == -1) { + /* The rsync manual says of fake-super, "any + * permission bits ... that would limit the + * owner's access" should be stored in the + * xattr rather than on the real file. So we + * need to make sure the owner has full + * permissions on the created directory. (This + * also avoids a permissions error when trying + * to read the xattrs on a mode 000 directory + * just after we create it.) */ + initial_mode = S_IRUSR|S_IWUSR|S_IXUSR; + } + else + initial_mode = file->mode & 0700; + if (do_mkdir(fname, initial_mode) == 0) file->flags |= FLAG_DIR_CREATED; goto cleanup; } @@ -1278,10 +1293,16 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, 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) { + + /* see comment above regarding fake-super */ + if (am_root == -1) + initial_mode = S_IRUSR|S_IWUSR|S_IXUSR; + else + initial_mode = file->mode; + if (real_ret != 0 && do_mkdir(fname,initial_mode) < 0 && errno != EEXIST) { if (!relative_paths || errno != ENOENT || make_path(fname, MKP_DROP_NAME | MKP_SKIP_SLASH) < 0 - || (do_mkdir(fname, file->mode) < 0 && errno != EEXIST)) { + || (do_mkdir(fname, initial_mode) < 0 && errno != EEXIST)) { rsyserr(FERROR_XFER, errno, "recv_generator: mkdir %s failed", full_fname(fname)); diff --git a/receiver.c b/receiver.c index d5a338c..efd88d8 100644 --- a/receiver.c +++ b/receiver.c @@ -25,6 +25,7 @@ extern int dry_run; extern int do_xfers; extern int am_server; +extern int am_root; extern int inc_recurse; extern int log_before_transfer; extern int stdout_format_has_i; @@ -165,6 +166,7 @@ int get_tmpname(char *fnametmp, const char *fname, BOOL make_unique) int open_tmpfile(char *fnametmp, const char *fname, struct file_struct *file) { int fd; + uint16 initial_mode; if (!get_tmpname(fnametmp, fname, False)) return -1; @@ -173,7 +175,20 @@ int open_tmpfile(char *fnametmp, const char *fname, struct file_struct *file) * access to ensure that there is no race condition. They will be * correctly updated after the right owner and group info is set. * (Thanks to snabb@epipe.fi for pointing this out.) */ - fd = do_mkstemp(fnametmp, (file->mode & INITACCESSPERMS) | S_IWUSR); + initial_mode = (file->mode & INITACCESSPERMS) | S_IWUSR; + + if (am_root == -1) { + /* The rsync manual says of fake-super, "any permission bits + * ... that would limit the owner's access" should be stored + * in the xattr rather than on the real file. So we need to + * make sure the owner has read permission on the created files + * (write permission was granted above). (This also avoids a + * permissions error when trying to read the xattrs on a mode + * 000 file just after we create it.) */ + initial_mode |= S_IRUSR; + } + + fd = do_mkstemp(fnametmp, initial_mode); #if 0 /* In most cases parent directories will already exist because their diff --git a/testsuite/fake-super.test b/testsuite/fake-super.test new file mode 100644 index 0000000..cffb239 --- /dev/null +++ b/testsuite/fake-super.test @@ -0,0 +1,222 @@ +#! /bin/sh + +# This program is distributable under the terms of the GNU GPL (see +# COPYING). + +# Test various aspects of --fake-super + +. "$suitedir/rsync.fns" + +# +# We want a local rsyncd running as root (so it can read mode 000 +# files), talking to a non-root "--fake-root" daemon. This is the +# non-root user we use to run the daemon... +# +nonrootuser=nobody + +id $nonrootuser \ + || test_skipped "Sorry, can't find '$nonrootuser' as a non-root user on your system" + +# +# Make sure our rsync supports xattrs +# +$RSYNC --version | grep ", xattrs" > /dev/null \ + || test_skipped "Rsync needs xattrs for fake-super tests" + +# +# Build a daemon config file +# +local_build_rsyncd_conf() { + # Build an appropriate configuration file + conf="$scratchdir/test-rsyncd.conf" + pidfile="$scratchdir/daemon.pid" + logfile="$scratchdir/daemon.log" + address=127.0.0.1 + port=2612 + hostname=`uname -n` + + cat > "$conf" < "$fromdir/test.txt" + chmod 000 "$fromdir/test.txt" + cat "$fromdir/test.txt" > /dev/null \ + || test_skipped "Can't read 'chmod 000' file (probably need root)" + + ls > "$fromdir/test2.txt" + chmod 000 "$fromdir/test2.txt" + chown $nonrootuser "$fromdir/test2.txt" + + # + # Mode 000 nested directories + # + mkdir "$fromdir/test.dir" + ls > "$fromdir/test.dir/test.txt" + chmod 000 "$fromdir/test.dir" + cat "$fromdir/test.dir/test.txt" > /dev/null \ + || test_skipped "Can't read file in 'chmod 000' dir (probably need root)" + + mkdir "$fromdir/test.dir/level2" + ls > "$fromdir/test.dir/level2/test.txt" + chmod 000 "$fromdir/test.dir/level2" + + # + # Mode 000 owned by non-root + # + mkdir "$fromdir/test2.dir" + ls > "$fromdir/test2.dir/test.txt" + chown $nonrootuser "$fromdir/test2.dir" + chown $nonrootuser "$fromdir/test2.dir/test.txt" + chmod 000 "$fromdir/test2.dir" + chmod 000 "$fromdir/test2.dir/test.txt" +} + +############################## + + +# +# Create source directory and keep a directory listing for later +# comparison as we're going to overwrite the source by bringing back the +# destination +# +local_create_test_source +( cd "$fromdir" && rsync_ls_lR . ) > "$tmpdir/ls-orig" + +# +# Our fake-root daemon +# +local_build_rsyncd_conf +local_start_daemon + +# +# Sync to dest... +# +$RSYNC -v -a --port $port "$fromdir/" "${address}::test-fake-super" \ + || test_fail "Error $? syncing to rsync daemon" + +# +# The rsync manual says of fake-super, "any permission bits ... that +# would limit the owner's access" should be stored in the xattr rather +# than on the real file. +# +# We test for that here (knowing that the original file/directory +# permissions were 000). +# +if [ `ls -l "$todir/test.txt" | sed 's/ .*//'` != "-rw-------" ] +then + test_fail "file: insufficient permissions set" +fi +if [ `ls -l "$todir/test2.txt" | sed 's/ .*//'` != "-rw-------" ] +then + test_fail "file: insufficient permissions set" +fi + +if [ `ls -ld "$todir/test.dir" | sed 's/ .*//'` != "drwx------" ] +then + test_fail "directory: insufficient permissions" +fi +if [ `ls -ld "$todir/test2.dir" | sed 's/ .*//'` != "drwx------" ] +then + test_fail "directory: insufficient permissions" +fi + +# +# Sync back to populated source... +# +$RSYNC -va --port $port "${address}::test-fake-super" "$fromdir/" \ + || test_fail "Error $? syncing to rsync daemon" + +( cd "$fromdir" && rsync_ls_lR . ) > "$tmpdir/ls-return" + +diff $diffopt "$tmpdir/ls-orig" "$tmpdir/ls-return" \ + || test_fail "Returned permissions don't match originals!" + +# +# Sync back to empty source... +# +rm -rf "$fromdir" +mkdir "$fromdir" + +$RSYNC -va --port $port "${address}::test-fake-super" "$fromdir/" \ + || test_fail "Error $? syncing to rsync daemon" + +( cd "$fromdir" && rsync_ls_lR . ) > "$tmpdir/ls-empty-return" + +diff $diffopt "$tmpdir/ls-orig" "$tmpdir/ls-empty-return" \ + || test_fail "Returned permissions to empty dir don't match originals!" + + +# +# Success! +# +exit 0 -- 1.6.3.3