| 1 | This patch adds a new option: --fake-super, which tells rsync to copy in a |
| 2 | fake super-user mode that stores various file attributes in an extended- |
| 3 | attribute value instead of as real file-system attributes. See the changes |
| 4 | to the manpages for details. |
| 5 | |
| 6 | To use this patch, run these commands for a successful build: |
| 7 | |
| 8 | patch -p1 <patches/acls.diff |
| 9 | patch -p1 <patches/xattrs.diff |
| 10 | patch -p1 <patches/fake-super.diff |
| 11 | ./prepare-source |
| 12 | ./configure --enable-xattr-support |
| 13 | make |
| 14 | |
| 15 | If you want ACL support too, use this configure command instead of the one |
| 16 | above: |
| 17 | |
| 18 | ./configure --enable-acl-support --enable-xattr-support |
| 19 | |
| 20 | --- old/backup.c |
| 21 | +++ new/backup.c |
| 22 | @@ -129,7 +129,7 @@ static int make_bak_dir(char *fullpath) |
| 23 | if (p >= rel) { |
| 24 | /* Try to transfer the directory settings of the |
| 25 | * actual dir that the files are coming from. */ |
| 26 | - if (do_stat(rel, &sx.st) < 0) { |
| 27 | + if (x_stat(rel, &sx.st, NULL) < 0) { |
| 28 | rsyserr(FERROR, errno, |
| 29 | "make_bak_dir stat %s failed", |
| 30 | full_fname(rel)); |
| 31 | @@ -200,7 +200,7 @@ static int keep_backup(char *fname) |
| 32 | int ret_code; |
| 33 | |
| 34 | /* return if no file to keep */ |
| 35 | - if (do_lstat(fname, &sx.st) < 0) |
| 36 | + if (x_lstat(fname, &sx.st, NULL) < 0) |
| 37 | return 1; |
| 38 | #ifdef SUPPORT_ACLS |
| 39 | sx.acc_acl = sx.def_acl = NULL; |
| 40 | --- old/clientserver.c |
| 41 | +++ new/clientserver.c |
| 42 | @@ -625,6 +625,11 @@ static int rsync_module(int f_in, int f_ |
| 43 | ret = parse_arguments(&argc, (const char ***) &argv, 0); |
| 44 | quiet = 0; /* Don't let someone try to be tricky. */ |
| 45 | |
| 46 | + if (lp_fake_super(i)) |
| 47 | + am_root = -1; |
| 48 | + else if (am_root < 0) /* Treat --fake-super from client as --super. */ |
| 49 | + am_root = 2; |
| 50 | + |
| 51 | if (filesfrom_fd == 0) |
| 52 | filesfrom_fd = f_in; |
| 53 | |
| 54 | --- old/flist.c |
| 55 | +++ new/flist.c |
| 56 | @@ -181,7 +181,7 @@ static int readlink_stat(const char *pat |
| 57 | } |
| 58 | return 0; |
| 59 | #else |
| 60 | - return do_stat(path, stp); |
| 61 | + return x_stat(path, stp, NULL); |
| 62 | #endif |
| 63 | } |
| 64 | |
| 65 | @@ -189,17 +189,17 @@ int link_stat(const char *path, STRUCT_S |
| 66 | { |
| 67 | #ifdef SUPPORT_LINKS |
| 68 | if (copy_links) |
| 69 | - return do_stat(path, stp); |
| 70 | - if (do_lstat(path, stp) < 0) |
| 71 | + return x_stat(path, stp, NULL); |
| 72 | + if (x_lstat(path, stp, NULL) < 0) |
| 73 | return -1; |
| 74 | if (follow_dirlinks && S_ISLNK(stp->st_mode)) { |
| 75 | STRUCT_STAT st; |
| 76 | - if (do_stat(path, &st) == 0 && S_ISDIR(st.st_mode)) |
| 77 | + if (x_stat(path, &st, NULL) == 0 && S_ISDIR(st.st_mode)) |
| 78 | *stp = st; |
| 79 | } |
| 80 | return 0; |
| 81 | #else |
| 82 | - return do_stat(path, stp); |
| 83 | + return x_stat(path, stp, NULL); |
| 84 | #endif |
| 85 | } |
| 86 | |
| 87 | @@ -234,26 +234,6 @@ static int is_excluded(char *fname, int |
| 88 | return 0; |
| 89 | } |
| 90 | |
| 91 | -static int to_wire_mode(mode_t mode) |
| 92 | -{ |
| 93 | -#ifdef SUPPORT_LINKS |
| 94 | -#if _S_IFLNK != 0120000 |
| 95 | - if (S_ISLNK(mode)) |
| 96 | - return (mode & ~(_S_IFMT)) | 0120000; |
| 97 | -#endif |
| 98 | -#endif |
| 99 | - return mode; |
| 100 | -} |
| 101 | - |
| 102 | -static mode_t from_wire_mode(int mode) |
| 103 | -{ |
| 104 | -#if _S_IFLNK != 0120000 |
| 105 | - if ((mode & (_S_IFMT)) == 0120000) |
| 106 | - return (mode & ~(_S_IFMT)) | _S_IFLNK; |
| 107 | -#endif |
| 108 | - return mode; |
| 109 | -} |
| 110 | - |
| 111 | static void send_directory(int f, struct file_list *flist, |
| 112 | char *fbuf, int len); |
| 113 | |
| 114 | @@ -793,7 +773,7 @@ struct file_struct *make_file(char *fnam |
| 115 | if (save_errno == ENOENT) { |
| 116 | #ifdef SUPPORT_LINKS |
| 117 | /* Avoid "vanished" error if symlink points nowhere. */ |
| 118 | - if (copy_links && do_lstat(thisname, &st) == 0 |
| 119 | + if (copy_links && x_lstat(thisname, &st, NULL) == 0 |
| 120 | && S_ISLNK(st.st_mode)) { |
| 121 | io_error |= IOERR_GENERAL; |
| 122 | rprintf(FERROR, "symlink has no referent: %s\n", |
| 123 | @@ -963,7 +943,7 @@ struct file_struct *make_file(char *fnam |
| 124 | int save_mode = file->mode; |
| 125 | file->mode = S_IFDIR; /* Find a directory with our name. */ |
| 126 | if (flist_find(the_file_list, file) >= 0 |
| 127 | - && do_stat(thisname, &st2) == 0 && S_ISDIR(st2.st_mode)) { |
| 128 | + && x_stat(thisname, &st2, NULL) == 0 && S_ISDIR(st2.st_mode)) { |
| 129 | file->modtime = st2.st_mtime; |
| 130 | file->length = st2.st_size; |
| 131 | file->mode = st2.st_mode; |
| 132 | --- old/loadparm.c |
| 133 | +++ new/loadparm.c |
| 134 | @@ -150,6 +150,7 @@ typedef struct |
| 135 | int syslog_facility; |
| 136 | int timeout; |
| 137 | |
| 138 | + BOOL fake_super; |
| 139 | BOOL ignore_errors; |
| 140 | BOOL ignore_nonreadable; |
| 141 | BOOL list; |
| 142 | @@ -197,6 +198,7 @@ static service sDefault = |
| 143 | /* syslog_facility; */ LOG_DAEMON, |
| 144 | /* timeout; */ 0, |
| 145 | |
| 146 | + /* fake_super; */ False, |
| 147 | /* ignore_errors; */ False, |
| 148 | /* ignore_nonreadable; */ False, |
| 149 | /* list; */ True, |
| 150 | @@ -298,6 +300,7 @@ static struct parm_struct parm_table[] = |
| 151 | {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress, NULL,0}, |
| 152 | {"exclude from", P_STRING, P_LOCAL, &sDefault.exclude_from, NULL,0}, |
| 153 | {"exclude", P_STRING, P_LOCAL, &sDefault.exclude, NULL,0}, |
| 154 | + {"fake super", P_BOOL, P_LOCAL, &sDefault.fake_super, NULL,0}, |
| 155 | {"filter", P_STRING, P_LOCAL, &sDefault.filter, NULL,0}, |
| 156 | {"gid", P_STRING, P_LOCAL, &sDefault.gid, NULL,0}, |
| 157 | {"hosts allow", P_STRING, P_LOCAL, &sDefault.hosts_allow, NULL,0}, |
| 158 | @@ -412,6 +415,7 @@ FN_LOCAL_INTEGER(lp_max_connections, max |
| 159 | FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity) |
| 160 | FN_LOCAL_INTEGER(lp_timeout, timeout) |
| 161 | |
| 162 | +FN_LOCAL_BOOL(lp_fake_super, fake_super) |
| 163 | FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors) |
| 164 | FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable) |
| 165 | FN_LOCAL_BOOL(lp_list, list) |
| 166 | @@ -816,7 +820,7 @@ BOOL lp_load(char *pszFname, int globals |
| 167 | |
| 168 | if (pszFname) |
| 169 | pstrcpy(n2,pszFname); |
| 170 | - else if (am_server && !am_root) |
| 171 | + else if (am_server && am_root <= 0) |
| 172 | pstrcpy(n2,RSYNCD_USERCONF); |
| 173 | else |
| 174 | pstrcpy(n2,RSYNCD_SYSCONF); |
| 175 | --- old/options.c |
| 176 | +++ new/options.c |
| 177 | @@ -73,7 +73,7 @@ int protocol_version = PROTOCOL_VERSION; |
| 178 | int sparse_files = 0; |
| 179 | int do_compression = 0; |
| 180 | int def_compress_level = Z_DEFAULT_COMPRESSION; |
| 181 | -int am_root = 0; |
| 182 | +int am_root = 0; /* 0 = normal, 1 = root, 2 = --super, -1 = --fake-super */ |
| 183 | int am_server = 0; |
| 184 | int am_sender = 0; |
| 185 | int am_generator = 0; |
| 186 | @@ -329,6 +329,9 @@ void usage(enum logcode F) |
| 187 | rprintf(F," -t, --times preserve times\n"); |
| 188 | rprintf(F," -O, --omit-dir-times omit directories when preserving times\n"); |
| 189 | rprintf(F," --super receiver attempts super-user activities\n"); |
| 190 | +#ifdef SUPPORT_XATTRS |
| 191 | + rprintf(F," --fake-super store/recover privileged attrs using xattrs\n"); |
| 192 | +#endif |
| 193 | rprintf(F," -S, --sparse handle sparse files efficiently\n"); |
| 194 | rprintf(F," -n, --dry-run show what would have been transferred\n"); |
| 195 | rprintf(F," -W, --whole-file copy files whole (without rsync algorithm)\n"); |
| 196 | @@ -453,6 +456,7 @@ static struct poptOption long_options[] |
| 197 | {"modify-window", 0, POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 }, |
| 198 | {"super", 0, POPT_ARG_VAL, &am_root, 2, 0, 0 }, |
| 199 | {"no-super", 0, POPT_ARG_VAL, &am_root, 0, 0, 0 }, |
| 200 | + {"fake-super", 0, POPT_ARG_VAL, &am_root, -1, 0, 0 }, |
| 201 | {"owner", 'o', POPT_ARG_VAL, &preserve_uid, 1, 0, 0 }, |
| 202 | {"no-owner", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 }, |
| 203 | {"no-o", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 }, |
| 204 | @@ -1178,6 +1182,14 @@ int parse_arguments(int *argc, const cha |
| 205 | } |
| 206 | #endif |
| 207 | |
| 208 | +#ifndef SUPPORT_XATTRS |
| 209 | + if (am_root < 0) { |
| 210 | + snprintf(err_buf, sizeof err_buf, |
| 211 | + "--fake-super requires an rsync with extended attributes enabled\n"); |
| 212 | + return 0; |
| 213 | + } |
| 214 | +#endif |
| 215 | + |
| 216 | if (write_batch && read_batch) { |
| 217 | snprintf(err_buf, sizeof err_buf, |
| 218 | "--write-batch and --read-batch can not be used together\n"); |
| 219 | --- old/rsync.c |
| 220 | +++ new/rsync.c |
| 221 | @@ -196,7 +196,9 @@ int set_file_attrs(char *fname, struct f |
| 222 | (long)sxp->st.st_gid, (long)file->gid); |
| 223 | } |
| 224 | } |
| 225 | - if (do_lchown(fname, |
| 226 | + if (am_root < 0) { |
| 227 | + ; |
| 228 | + } else if (do_lchown(fname, |
| 229 | change_uid ? file->uid : sxp->st.st_uid, |
| 230 | change_gid ? file->gid : sxp->st.st_gid) != 0) { |
| 231 | /* shouldn't have attempted to change uid or gid |
| 232 | @@ -205,7 +207,7 @@ int set_file_attrs(char *fname, struct f |
| 233 | change_uid ? "chown" : "chgrp", |
| 234 | full_fname(fname)); |
| 235 | goto cleanup; |
| 236 | - } |
| 237 | + } else |
| 238 | /* a lchown had been done - we have to re-stat if the |
| 239 | * destination had the setuid or setgid bits set due |
| 240 | * to the side effect of the chown call */ |
| 241 | @@ -222,6 +224,8 @@ int set_file_attrs(char *fname, struct f |
| 242 | #ifdef SUPPORT_XATTRS |
| 243 | if (preserve_xattrs && set_xattr(fname, file, sxp) == 0) |
| 244 | updated = 1; |
| 245 | + if (am_root < 0) |
| 246 | + set_stat_xattr(fname, file); |
| 247 | #endif |
| 248 | #ifdef SUPPORT_ACLS |
| 249 | /* It's OK to call set_acl() now, even for a dir, as the generator |
| 250 | @@ -236,7 +240,7 @@ int set_file_attrs(char *fname, struct f |
| 251 | |
| 252 | #ifdef HAVE_CHMOD |
| 253 | if ((sxp->st.st_mode & CHMOD_BITS) != (new_mode & CHMOD_BITS)) { |
| 254 | - int ret = do_chmod(fname, new_mode); |
| 255 | + int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode); |
| 256 | if (ret < 0) { |
| 257 | rsyserr(FERROR, errno, |
| 258 | "failed to set permissions on %s", |
| 259 | --- old/rsync.h |
| 260 | +++ new/rsync.h |
| 261 | @@ -707,6 +707,12 @@ typedef struct { |
| 262 | |
| 263 | #include "proto.h" |
| 264 | |
| 265 | +#ifndef SUPPORT_XATTRS |
| 266 | +#define x_stat(fn,fst,xst) do_stat(fn,fst) |
| 267 | +#define x_lstat(fn,fst,xst) do_lstat(fn,fst) |
| 268 | +#define x_fstat(fd,fst,xst) do_fstat(fd,fst) |
| 269 | +#endif |
| 270 | + |
| 271 | /* We have replacement versions of these if they're missing. */ |
| 272 | #ifndef HAVE_ASPRINTF |
| 273 | int asprintf(char **ptr, const char *format, ...); |
| 274 | @@ -924,3 +930,23 @@ int inet_pton(int af, const char *src, v |
| 275 | #ifdef MAINTAINER_MODE |
| 276 | const char *get_panic_action(void); |
| 277 | #endif |
| 278 | + |
| 279 | +static inline int to_wire_mode(mode_t mode) |
| 280 | +{ |
| 281 | +#ifdef SUPPORT_LINKS |
| 282 | +#if _S_IFLNK != 0120000 |
| 283 | + if (S_ISLNK(mode)) |
| 284 | + return (mode & ~(_S_IFMT)) | 0120000; |
| 285 | +#endif |
| 286 | +#endif |
| 287 | + return mode; |
| 288 | +} |
| 289 | + |
| 290 | +static inline mode_t from_wire_mode(int mode) |
| 291 | +{ |
| 292 | +#if _S_IFLNK != 0120000 |
| 293 | + if ((mode & (_S_IFMT)) == 0120000) |
| 294 | + return (mode & ~(_S_IFMT)) | _S_IFLNK; |
| 295 | +#endif |
| 296 | + return mode; |
| 297 | +} |
| 298 | --- old/rsync.yo |
| 299 | +++ new/rsync.yo |
| 300 | @@ -333,6 +333,7 @@ to the detailed description below for a |
| 301 | -t, --times preserve times |
| 302 | -O, --omit-dir-times omit directories when preserving times |
| 303 | --super receiver attempts super-user activities |
| 304 | + --fake-super store/recover privileged attrs using xattrs |
| 305 | -S, --sparse handle sparse files efficiently |
| 306 | -n, --dry-run show what would have been transferred |
| 307 | -W, --whole-file copy files whole (without rsync algorithm) |
| 308 | @@ -846,7 +847,7 @@ permission value can be applied to the f |
| 309 | dit(bf(-o, --owner)) This option causes rsync to set the owner of the |
| 310 | destination file to be the same as the source file, but only if the |
| 311 | receiving rsync is being run as the super-user (see also the bf(--super) |
| 312 | -option to force rsync to attempt super-user activities). |
| 313 | +and bf(--fake-super) options). |
| 314 | Without this option, the owner is set to the invoking user on the |
| 315 | receiving side. |
| 316 | |
| 317 | @@ -869,7 +870,7 @@ default, but may fall back to using the |
| 318 | dit(bf(--devices)) This option causes rsync to transfer character and |
| 319 | block device files to the remote system to recreate these devices. |
| 320 | This option has no effect if the receiving rsync is not run as the |
| 321 | -super-user and bf(--super) is not specified. |
| 322 | +super-user (see also the bf(--super) and bf(--fake-super) options). |
| 323 | |
| 324 | dit(bf(--specials)) This option causes rsync to transfer special files |
| 325 | such as named sockets and fifos. |
| 326 | @@ -899,6 +900,33 @@ also for ensuring that you will get erro |
| 327 | being running as the super-user. To turn off super-user activities, the |
| 328 | super-user can use bf(--no-super). |
| 329 | |
| 330 | +dit(bf(--fake-super)) When this option is enabled, rsync simulates |
| 331 | +super-user activities by saving/restoring the privileged attributes via a |
| 332 | +special extended attribute that is attached to each file (as needed). This |
| 333 | +includes the file's owner and group (if it is not the default), the file's |
| 334 | +device info (device & special files are created as empty text files), and |
| 335 | +any permission bits that we won't allow to be set on the real file (e.g. |
| 336 | +the real file gets u-s,g-s,o-t for safety) or that would limit the owner's |
| 337 | +access (since the real super-user can always access/change a file or |
| 338 | +directory, the files we create can always be accessed/changed by the |
| 339 | +creating user). |
| 340 | + |
| 341 | +The bf(--fake-super) option only affects the side where the option is used. |
| 342 | +To affect the remote side of a remote-shell connection, specify an rsync |
| 343 | +path: |
| 344 | + |
| 345 | +quote(tt( rsync -av --rsync-path="rsync --fake-super" /src/ host:/dest/)) |
| 346 | + |
| 347 | +Since there is only one "side" in a local copy, this option affects both |
| 348 | +the sending and recieving of files. You'll need to specify a copy using |
| 349 | +"localhost" if you need to avoid this. Note, however, that it is always |
| 350 | +safe to copy from some non-fake-super files into some fake-super files |
| 351 | +using a local bf(--fake-super) command because the non-fake source files |
| 352 | +will just have their normal attributes. |
| 353 | + |
| 354 | +See also the "fake super" setting in the daemon's rsyncd.conf file. |
| 355 | +This option is overridden by both bf(--super) and bf(--no-super). |
| 356 | + |
| 357 | dit(bf(-S, --sparse)) Try to handle sparse files efficiently so they take |
| 358 | up less space on the destination. Conflicts with bf(--inplace) because it's |
| 359 | not possible to overwrite data in a sparse fashion. |
| 360 | --- old/rsyncd.conf.yo |
| 361 | +++ new/rsyncd.conf.yo |
| 362 | @@ -226,6 +226,11 @@ file transfers to and from that module s |
| 363 | was run as root. This complements the "uid" option. The default is gid -2, |
| 364 | which is normally the group "nobody". |
| 365 | |
| 366 | +dit(bf(fake super)) Setting "fake super = yes" for a module causes the |
| 367 | +daemon side to behave as if the bf(--fake-user) command-line option had |
| 368 | +been specified. This allows the full attributes of a file to be stored |
| 369 | +without having to have the daemon actually running as root. |
| 370 | + |
| 371 | dit(bf(filter)) The "filter" option allows you to specify a space-separated |
| 372 | list of filter rules that the daemon will not allow to be read or written. |
| 373 | This is only superficially equivalent to the client specifying these |
| 374 | --- old/syscall.c |
| 375 | +++ new/syscall.c |
| 376 | @@ -28,6 +28,7 @@ |
| 377 | #endif |
| 378 | |
| 379 | extern int dry_run; |
| 380 | +extern int am_root; |
| 381 | extern int read_only; |
| 382 | extern int list_only; |
| 383 | extern int preserve_perms; |
| 384 | @@ -79,6 +80,15 @@ int do_mknod(char *pathname, mode_t mode |
| 385 | { |
| 386 | if (dry_run) return 0; |
| 387 | RETURN_ERROR_IF_RO_OR_LO; |
| 388 | + |
| 389 | + /* For --fake-super, we create a normal file with mode 0600. */ |
| 390 | + if (am_root < 0) { |
| 391 | + int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR); |
| 392 | + if (fd < 0 || close(fd) < 0) |
| 393 | + return -1; |
| 394 | + return 0; |
| 395 | + } |
| 396 | + |
| 397 | #if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO |
| 398 | if (S_ISFIFO(mode)) |
| 399 | return mkfifo(pathname, mode); |
| 400 | --- old/t_unsafe.c |
| 401 | +++ new/t_unsafe.c |
| 402 | @@ -24,7 +24,11 @@ |
| 403 | |
| 404 | #include "rsync.h" |
| 405 | |
| 406 | -int dry_run, read_only, list_only, verbose; |
| 407 | +int dry_run = 0; |
| 408 | +int am_root = 0; |
| 409 | +int read_only = 0; |
| 410 | +int list_only = 0; |
| 411 | +int verbose = 0; |
| 412 | int preserve_perms = 0; |
| 413 | |
| 414 | int |
| 415 | --- old/tls.c |
| 416 | +++ new/tls.c |
| 417 | @@ -39,6 +39,7 @@ |
| 418 | |
| 419 | /* These are to make syscall.o shut up. */ |
| 420 | int dry_run = 0; |
| 421 | +int am_root = 0; |
| 422 | int read_only = 1; |
| 423 | int list_only = 0; |
| 424 | int preserve_perms = 0; |
| 425 | --- old/trimslash.c |
| 426 | +++ new/trimslash.c |
| 427 | @@ -23,6 +23,7 @@ |
| 428 | |
| 429 | /* These are to make syscall.o shut up. */ |
| 430 | int dry_run = 0; |
| 431 | +int am_root = 0; |
| 432 | int read_only = 1; |
| 433 | int list_only = 0; |
| 434 | int preserve_perms = 0; |
| 435 | --- old/xattr.c |
| 436 | +++ new/xattr.c |
| 437 | @@ -43,11 +43,16 @@ extern unsigned int file_struct_len; |
| 438 | #define SPRE_LEN ((int)sizeof SYSTEM_PREFIX - 1) |
| 439 | |
| 440 | #ifdef HAVE_LINUX_XATTRS |
| 441 | -#define RPRE_LEN 0 |
| 442 | +#define RSYNC_PREFIX USER_PREFIX "rsync." |
| 443 | #else |
| 444 | #define RSYNC_PREFIX "rsync." |
| 445 | -#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1) |
| 446 | #endif |
| 447 | +#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1) |
| 448 | + |
| 449 | +#define XSTAT_ATTR RSYNC_PREFIX "%stat" |
| 450 | +#define XSTAT_LEN ((int)sizeof XSTAT_ATTR - 1) |
| 451 | + |
| 452 | +#define CENT_POS RPRE_LEN |
| 453 | |
| 454 | typedef struct { |
| 455 | char *datum, *name; |
| 456 | @@ -148,6 +153,10 @@ static int rsync_xal_get(const char *fna |
| 457 | continue; |
| 458 | #endif |
| 459 | |
| 460 | + if (am_root < 0 && name_len == XSTAT_LEN + 1 |
| 461 | + && name[CENT_POS] == '%' && strcmp(name, XSTAT_ATTR) == 0) |
| 462 | + continue; |
| 463 | + |
| 464 | datum_len = sys_lgetxattr(fname, name, NULL, 0); |
| 465 | if (datum_len < 0) { |
| 466 | if (errno == ENOTSUP) |
| 467 | @@ -177,6 +186,13 @@ static int rsync_xal_get(const char *fna |
| 468 | return -1; |
| 469 | } |
| 470 | } |
| 471 | +#ifdef HAVE_LINUX_XATTRS |
| 472 | + if (am_root < 0 && name_len > RPRE_LEN |
| 473 | + && HAS_PREFIX(name, RSYNC_PREFIX)) { |
| 474 | + name += RPRE_LEN; |
| 475 | + name_len -= RPRE_LEN; |
| 476 | + } |
| 477 | +#endif |
| 478 | rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL); |
| 479 | rxas->name = ptr + datum_len; |
| 480 | rxas->datum = ptr; |
| 481 | @@ -299,10 +315,8 @@ void receive_xattr(struct file_struct *f |
| 482 | size_t name_len = read_int(f); |
| 483 | size_t datum_len = read_int(f); |
| 484 | size_t extra_len = am_root < 0 ? RPRE_LEN : 0; |
| 485 | -#ifndef HAVE_LINUX_XATTRS |
| 486 | if (datum_len + extra_len < datum_len) |
| 487 | out_of_memory("receive_xattr"); /* overflow */ |
| 488 | -#endif |
| 489 | if (name_len + datum_len + extra_len < name_len) |
| 490 | out_of_memory("receive_xattr"); /* overflow */ |
| 491 | ptr = new_array(char, name_len + datum_len + extra_len); |
| 492 | @@ -313,6 +327,10 @@ void receive_xattr(struct file_struct *f |
| 493 | read_buf(f, ptr, datum_len); |
| 494 | #ifdef HAVE_LINUX_XATTRS |
| 495 | /* Non-root can only save the user namespace. */ |
| 496 | + if (am_root < 0 && !HAS_PREFIX(name, USER_PREFIX)) { |
| 497 | + name -= RPRE_LEN; |
| 498 | + memcpy(name, RSYNC_PREFIX, RPRE_LEN); |
| 499 | + } else |
| 500 | if (!am_root && !HAS_PREFIX(name, USER_PREFIX)) { |
| 501 | free(ptr); |
| 502 | continue; |
| 503 | @@ -333,6 +351,11 @@ void receive_xattr(struct file_struct *f |
| 504 | continue; |
| 505 | } |
| 506 | #endif |
| 507 | + if (am_root < 0 && name_len == XSTAT_LEN + 1 |
| 508 | + && name[CENT_POS] == '%' && strcmp(name, XSTAT_ATTR) == 0) { |
| 509 | + free(ptr); |
| 510 | + continue; |
| 511 | + } |
| 512 | rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count); |
| 513 | rxa->name = name; |
| 514 | rxa->datum = ptr; |
| 515 | @@ -412,4 +435,146 @@ int set_xattr(const char *fname, const s |
| 516 | return rsync_xal_set(fname, lst + ndx); /* TODO: This needs to return 1 if no xattrs changed! */ |
| 517 | } |
| 518 | |
| 519 | +int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *fst, STRUCT_STAT *xst) |
| 520 | +{ |
| 521 | + int mode, rdev_major, rdev_minor, uid, gid, len; |
| 522 | + char buf[256]; |
| 523 | + |
| 524 | + if (am_root >= 0 || IS_DEVICE(fst->st_mode) || IS_SPECIAL(fst->st_mode)) |
| 525 | + return -1; |
| 526 | + |
| 527 | + if (xst) |
| 528 | + *xst = *fst; |
| 529 | + else |
| 530 | + xst = fst; |
| 531 | + if (fname) { |
| 532 | + fd = -1; |
| 533 | + len = sys_lgetxattr(fname, XSTAT_ATTR, buf, sizeof buf - 1); |
| 534 | + } else { |
| 535 | + fname = "fd"; |
| 536 | + len = sys_fgetxattr(fd, XSTAT_ATTR, buf, sizeof buf - 1); |
| 537 | + } |
| 538 | + if (len >= (int)sizeof buf) { |
| 539 | + len = -1; |
| 540 | + errno = ERANGE; |
| 541 | + } |
| 542 | + if (len < 0) { |
| 543 | + if (errno == ENOTSUP || errno == ENOATTR) |
| 544 | + return -1; |
| 545 | + if (errno == EPERM && S_ISLNK(fst->st_mode)) { |
| 546 | + xst->st_uid = 0; |
| 547 | + xst->st_gid = 0; |
| 548 | + return 0; |
| 549 | + } |
| 550 | + rsyserr(FERROR, errno, "failed to read xattr %s for %s", |
| 551 | + XSTAT_ATTR, full_fname(fname)); |
| 552 | + return -1; |
| 553 | + } |
| 554 | + buf[len] = '\0'; |
| 555 | + |
| 556 | + if (sscanf(buf, "%o %d,%d %d:%d", |
| 557 | + &mode, &rdev_major, &rdev_minor, &uid, &gid) != 5) { |
| 558 | + rprintf(FERROR, "Corrupt %s xattr attached to %s: \"%s\"\n", |
| 559 | + XSTAT_ATTR, full_fname(fname), buf); |
| 560 | + exit_cleanup(RERR_FILEIO); |
| 561 | + } |
| 562 | + |
| 563 | + xst->st_mode = from_wire_mode(mode); |
| 564 | + xst->st_rdev = MAKEDEV(rdev_major, rdev_minor); |
| 565 | + xst->st_uid = uid; |
| 566 | + xst->st_gid = gid; |
| 567 | + |
| 568 | + return 0; |
| 569 | +} |
| 570 | + |
| 571 | +int set_stat_xattr(const char *fname, struct file_struct *file) |
| 572 | +{ |
| 573 | + STRUCT_STAT fst, xst; |
| 574 | + dev_t rdev; |
| 575 | + mode_t mode; |
| 576 | + |
| 577 | + if (dry_run) |
| 578 | + return 0; |
| 579 | + |
| 580 | + if (read_only || list_only) { |
| 581 | + rsyserr(FERROR, EROFS, "failed to write xattr %s for %s", |
| 582 | + XSTAT_ATTR, full_fname(fname)); |
| 583 | + return -1; |
| 584 | + } |
| 585 | + |
| 586 | + if (x_lstat(fname, &fst, &xst) < 0) { |
| 587 | + rsyserr(FERROR, errno, "failed to re-stat %s", |
| 588 | + full_fname(fname)); |
| 589 | + return -1; |
| 590 | + } |
| 591 | + |
| 592 | + if (IS_DEVICE(file->mode) || IS_SPECIAL(file->mode)) |
| 593 | + rdev = file->u.rdev; |
| 594 | + else |
| 595 | + rdev = 0; |
| 596 | + |
| 597 | + /* Dump the special permissions and enable full owner access. */ |
| 598 | + mode = (fst.st_mode & ~CHMOD_BITS) | (file->mode & ACCESSPERMS) |
| 599 | + | (S_ISDIR(fst.st_mode) ? 0700 : 0600); |
| 600 | + if (fst.st_mode != mode) |
| 601 | + do_chmod(fname, mode); |
| 602 | + if (!IS_DEVICE(fst.st_mode) && !IS_SPECIAL(fst.st_mode)) |
| 603 | + fst.st_rdev = 0; /* just in case */ |
| 604 | + |
| 605 | + if (mode == file->mode && fst.st_rdev == rdev |
| 606 | + && fst.st_uid == file->uid && fst.st_gid == file->gid) { |
| 607 | + /* xst.st_mode will be 0 if there's no current stat xattr */ |
| 608 | + if (xst.st_mode && sys_lremovexattr(fname, XSTAT_ATTR) < 0) { |
| 609 | + rsyserr(FERROR, errno, |
| 610 | + "delete of stat xattr failed for %s", |
| 611 | + full_fname(fname)); |
| 612 | + return -1; |
| 613 | + } |
| 614 | + return 0; |
| 615 | + } |
| 616 | + |
| 617 | + if (xst.st_mode != file->mode || xst.st_rdev != rdev |
| 618 | + || xst.st_uid != file->uid || xst.st_gid != file->gid) { |
| 619 | + char buf[256]; |
| 620 | + int len = snprintf(buf, sizeof buf, "%o %u,%u %u:%u", |
| 621 | + to_wire_mode(file->mode) & (_S_IFMT|CHMOD_BITS), |
| 622 | + (int)major(rdev), (int)minor(rdev), |
| 623 | + (int)file->uid, (int)file->gid); |
| 624 | + if (sys_lsetxattr(fname, XSTAT_ATTR, buf, len) < 0) { |
| 625 | + if (errno == EPERM && S_ISLNK(fst.st_mode)) |
| 626 | + return 0; |
| 627 | + rsyserr(FERROR, errno, |
| 628 | + "failed to write xattr %s for %s", |
| 629 | + XSTAT_ATTR, full_fname(fname)); |
| 630 | + return -1; |
| 631 | + } |
| 632 | + } |
| 633 | + |
| 634 | + return 0; |
| 635 | +} |
| 636 | + |
| 637 | +int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst) |
| 638 | +{ |
| 639 | + int ret = do_stat(fname, fst); |
| 640 | + if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst) |
| 641 | + xst->st_mode = 0; |
| 642 | + return ret; |
| 643 | +} |
| 644 | + |
| 645 | +int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst) |
| 646 | +{ |
| 647 | + int ret = do_lstat(fname, fst); |
| 648 | + if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst) |
| 649 | + xst->st_mode = 0; |
| 650 | + return ret; |
| 651 | +} |
| 652 | + |
| 653 | +int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst) |
| 654 | +{ |
| 655 | + int ret = do_fstat(fd, fst); |
| 656 | + if ((ret < 0 || get_stat_xattr(NULL, fd, fst, xst) < 0) && xst) |
| 657 | + xst->st_mode = 0; |
| 658 | + return ret; |
| 659 | +} |
| 660 | + |
| 661 | #endif /* SUPPORT_XATTRS */ |