diff --git a/acls.c b/acls.c index 8d97df6..24e8cf6 100644 --- a/acls.c +++ b/acls.c @@ -31,6 +31,7 @@ extern int list_only; extern int orig_umask; extern int numeric_ids; extern int inc_recurse; +extern int protocol_version; extern int preserve_devices; extern int preserve_specials; @@ -112,6 +113,18 @@ static const char *str_acl_type(SMB_ACL_TYPE_T type) return "unknown ACL type!"; } +#define OTHER_TYPE(t) (SMB_ACL_TYPE_ACCESS+SMB_ACL_TYPE_DEFAULT-(t)) +#define BUMP_TYPE(t) ((t = OTHER_TYPE(t)) == SMB_ACL_TYPE_DEFAULT) + +static int old_count_racl_entries(const rsync_acl *racl) +{ + return racl->names.count + + (racl->user_obj != NO_ENTRY) + + (racl->group_obj != NO_ENTRY) + + (racl->mask_obj != NO_ENTRY) + + (racl->other_obj != NO_ENTRY); +} + static int calc_sacl_entries(const rsync_acl *racl) { /* A System ACL always gets user/group/other permission entries. */ @@ -581,6 +594,96 @@ int get_acl(const char *fname, stat_x *sxp) return 0; } +/* === OLD Send functions === */ + +/* Send the ida list over the file descriptor. */ +static void old_send_ida_entries(int f, const ida_entries *idal, char tag_char) +{ + id_access *ida; + size_t count = idal->count; + for (ida = idal->idas; count--; ida++) { + if (tag_char == 'U') { + if (!(ida->access & NAME_IS_USER)) + continue; + add_uid(ida->id); + } else { + if (ida->access & NAME_IS_USER) + continue; + add_gid(ida->id); + } + write_byte(f, tag_char); + write_byte(f, ida->access); + write_int(f, ida->id); + } +} + +/* Send an rsync ACL over the file descriptor. */ +static void old_send_rsync_acl(int f, const rsync_acl *racl) +{ + size_t count = old_count_racl_entries(racl); + write_int(f, count); + if (racl->user_obj != NO_ENTRY) { + write_byte(f, 'u'); + write_byte(f, racl->user_obj); + } + old_send_ida_entries(f, &racl->names, 'U'); + if (racl->group_obj != NO_ENTRY) { + write_byte(f, 'g'); + write_byte(f, racl->group_obj); + } + old_send_ida_entries(f, &racl->names, 'G'); + if (racl->mask_obj != NO_ENTRY) { + write_byte(f, 'm'); + write_byte(f, racl->mask_obj); + } + if (racl->other_obj != NO_ENTRY) { + write_byte(f, 'o'); + write_byte(f, racl->other_obj); + } +} + +/* Send the ACL from the stat_x structure down the indicated file descriptor. + * This also frees the ACL data. */ +void old_send_acl(stat_x *sxp, int f) +{ + SMB_ACL_TYPE_T type; + rsync_acl *racl, *new_racl; + item_list *racl_list; + int ndx; + + type = SMB_ACL_TYPE_ACCESS; + racl = sxp->acc_acl; + racl_list = &access_acl_list; + do { + if (!racl) { + racl = new(rsync_acl); + if (!racl) + out_of_memory("send_acl"); + *racl = empty_rsync_acl; + if (type == SMB_ACL_TYPE_ACCESS) { + rsync_acl_fake_perms(racl, sxp->st.st_mode); + sxp->acc_acl = racl; + } else + sxp->def_acl = racl; + } + + if ((ndx = find_matching_rsync_acl(racl, type, racl_list)) != -1) { + write_byte(f, type == SMB_ACL_TYPE_ACCESS ? 'a' : 'd'); + write_int(f, ndx); + } else { + new_racl = EXPAND_ITEM_LIST(racl_list, rsync_acl, 1000); + write_byte(f, type == SMB_ACL_TYPE_ACCESS ? 'A' : 'D'); + old_send_rsync_acl(f, racl); + *new_racl = *racl; + *racl = empty_rsync_acl; + } + racl = sxp->def_acl; + racl_list = &default_acl_list; + } while (BUMP_TYPE(type) && S_ISDIR(sxp->st.st_mode)); + + free_acl(sxp); +} + /* === Send functions === */ /* Send the ida list over the file descriptor. */ @@ -656,6 +759,11 @@ static void send_rsync_acl(int f, rsync_acl *racl, SMB_ACL_TYPE_T type, * This also frees the ACL data. */ void send_acl(int f, stat_x *sxp) { + if (protocol_version < 30) { + old_send_acl(sxp, f); + return; + } + if (!sxp->acc_acl) { sxp->acc_acl = create_racl(); rsync_acl_fake_perms(sxp->acc_acl, sxp->st.st_mode); @@ -673,6 +781,160 @@ void send_acl(int f, stat_x *sxp) } } +/* === OLD Receive functions */ + +static void old_recv_rsync_acl(rsync_acl *racl, int f) +{ + static item_list temp_ida_list = EMPTY_ITEM_LIST; + SMB_ACL_TAG_T tag_type = 0; + uchar computed_mask_bits = 0; + id_access *ida; + size_t count; + + if (!(count = read_int(f))) + return; + + while (count--) { + char tag = read_byte(f); + uchar access = read_byte(f); + if (access & ~ (4 | 2 | 1)) { + rprintf(FERROR, "old_recv_rsync_acl: bogus permset %o\n", + access); + exit_cleanup(RERR_STREAMIO); + } + switch (tag) { + case 'u': + if (racl->user_obj != NO_ENTRY) { + rprintf(FERROR, "old_recv_rsync_acl: error: duplicate USER_OBJ entry\n"); + exit_cleanup(RERR_STREAMIO); + } + racl->user_obj = access; + continue; + case 'U': + tag_type = SMB_ACL_USER; + break; + case 'g': + if (racl->group_obj != NO_ENTRY) { + rprintf(FERROR, "old_recv_rsync_acl: error: duplicate GROUP_OBJ entry\n"); + exit_cleanup(RERR_STREAMIO); + } + racl->group_obj = access; + continue; + case 'G': + tag_type = SMB_ACL_GROUP; + break; + case 'm': + if (racl->mask_obj != NO_ENTRY) { + rprintf(FERROR, "old_recv_rsync_acl: error: duplicate MASK entry\n"); + exit_cleanup(RERR_STREAMIO); + } + racl->mask_obj = access; + continue; + case 'o': + if (racl->other_obj != NO_ENTRY) { + rprintf(FERROR, "old_recv_rsync_acl: error: duplicate OTHER entry\n"); + exit_cleanup(RERR_STREAMIO); + } + racl->other_obj = access; + continue; + default: + rprintf(FERROR, "old_recv_rsync_acl: unknown tag %c\n", + tag); + exit_cleanup(RERR_STREAMIO); + } + ida = EXPAND_ITEM_LIST(&temp_ida_list, id_access, -10); + ida->access = access | (tag_type == SMB_ACL_USER ? NAME_IS_USER : 0); + ida->id = read_int(f); + computed_mask_bits |= access; + } + + /* Transfer the count id_access items out of the temp_ida_list + * into the names ida_entries list in racl. */ + if (temp_ida_list.count) { +#ifdef SMB_ACL_NEED_SORT + if (temp_ida_list.count > 1) { + qsort(temp_ida_list.items, temp_ida_list.count, + sizeof (id_access), id_access_sorter); + } +#endif + if (!(racl->names.idas = new_array(id_access, temp_ida_list.count))) + out_of_memory("unpack_smb_acl"); + memcpy(racl->names.idas, temp_ida_list.items, + temp_ida_list.count * sizeof (id_access)); + } else + racl->names.idas = NULL; + + racl->names.count = temp_ida_list.count; + + /* Truncate the temporary list now that its idas have been saved. */ + temp_ida_list.count = 0; + + if (!racl->names.count) { + /* If we received a superfluous mask, throw it away. */ + if (racl->mask_obj != NO_ENTRY) { + /* Mask off the group perms with it first. */ + racl->group_obj &= racl->mask_obj | NO_ENTRY; + racl->mask_obj = NO_ENTRY; + } + } else if (racl->mask_obj == NO_ENTRY) /* Must be non-empty with lists. */ + racl->mask_obj = (computed_mask_bits | racl->group_obj) & 7; +} + +/* Receive the ACL info the sender has included for this file-list entry. */ +void old_recv_acl(struct file_struct *file, int f) +{ + SMB_ACL_TYPE_T type; + item_list *racl_list; + + if (S_ISLNK(file->mode)) + return; + + type = SMB_ACL_TYPE_ACCESS; + racl_list = &access_acl_list; + do { + char tag = read_byte(f); + int ndx; + + if (tag == 'A' || tag == 'a') { + if (type != SMB_ACL_TYPE_ACCESS) { + rprintf(FERROR, "receive_acl %s: duplicate access ACL\n", + f_name(file, NULL)); + exit_cleanup(RERR_STREAMIO); + } + } else if (tag == 'D' || tag == 'd') { + if (type == SMB_ACL_TYPE_ACCESS) { + rprintf(FERROR, "receive_acl %s: expecting access ACL; got default\n", + f_name(file, NULL)); + exit_cleanup(RERR_STREAMIO); + } + } else { + rprintf(FERROR, "receive_acl %s: unknown ACL type tag: %c\n", + f_name(file, NULL), tag); + exit_cleanup(RERR_STREAMIO); + } + if (tag == 'A' || tag == 'D') { + acl_duo *duo_item; + ndx = racl_list->count; + duo_item = EXPAND_ITEM_LIST(racl_list, acl_duo, 1000); + duo_item->racl = empty_rsync_acl; + old_recv_rsync_acl(&duo_item->racl, f); + duo_item->sacl = NULL; + } else { + ndx = read_int(f); + if (ndx < 0 || (size_t)ndx >= racl_list->count) { + rprintf(FERROR, "receive_acl %s: %s ACL index %d out of range\n", + f_name(file, NULL), str_acl_type(type), ndx); + exit_cleanup(RERR_STREAMIO); + } + } + if (type == SMB_ACL_TYPE_ACCESS) + F_ACL(file) = ndx; + else + F_DIR_DEFACL(file) = ndx; + racl_list = &default_acl_list; + } while (BUMP_TYPE(type) && S_ISDIR(file->mode)); +} + /* === Receive functions === */ static uint32 recv_acl_access(int f, uchar *name_follows_ptr) @@ -794,6 +1056,11 @@ static int recv_rsync_acl(int f, item_list *racl_list, SMB_ACL_TYPE_T type, mode /* Receive the ACL info the sender has included for this file-list entry. */ void receive_acl(int f, struct file_struct *file) { + if (protocol_version < 30) { + old_recv_acl(file, f); + return; + } + F_ACL(file) = recv_rsync_acl(f, &access_acl_list, SMB_ACL_TYPE_ACCESS, file->mode); if (S_ISDIR(file->mode)) diff --git a/compat.c b/compat.c index 8d755c6..627c520 100644 --- a/compat.c +++ b/compat.c @@ -205,20 +205,6 @@ void setup_protocol(int f_out,int f_in) if (protocol_version < 30) { if (append_mode == 1) append_mode = 2; - if (preserve_acls && !local_server) { - rprintf(FERROR, - "--acls requires protocol 30 or higher" - " (negotiated %d).\n", - protocol_version); - exit_cleanup(RERR_PROTOCOL); - } - if (preserve_xattrs && !local_server) { - rprintf(FERROR, - "--xattrs requires protocol 30 or higher" - " (negotiated %d).\n", - protocol_version); - exit_cleanup(RERR_PROTOCOL); - } } if (delete_mode && !(delete_before+delete_during+delete_after)) { diff --git a/generator.c b/generator.c index a112da6..13c3caf 100644 --- a/generator.c +++ b/generator.c @@ -39,6 +39,7 @@ extern int preserve_acls; extern int preserve_xattrs; extern int preserve_links; extern int preserve_devices; +extern int copy_devices; extern int preserve_specials; extern int preserve_hard_links; extern int preserve_executability; @@ -1657,7 +1658,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx, goto cleanup; } - if (!S_ISREG(file->mode)) { + if (!(S_ISREG(file->mode) || (copy_devices && IS_DEVICE(file->mode)))) { if (solo_file) fname = f_name(file, NULL); rprintf(FINFO, "skipping non-regular file \"%s\"\n", fname); diff --git a/options.c b/options.c index 1c5b42d..195672e 100644 --- a/options.c +++ b/options.c @@ -50,6 +50,7 @@ int append_mode = 0; int keep_dirlinks = 0; int copy_dirlinks = 0; int copy_links = 0; +int copy_devices = 0; int preserve_links = 0; int preserve_hard_links = 0; int preserve_acls = 0; @@ -705,6 +706,7 @@ void usage(enum logcode F) 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"); + rprintf(F," --copy-devices copy device contents as regular file\n"); rprintf(F," --specials preserve special files\n"); rprintf(F," -D same as --devices --specials\n"); rprintf(F," -t, --times preserve modification times\n"); @@ -887,6 +889,7 @@ static struct poptOption long_options[] = { {"no-D", 0, POPT_ARG_NONE, 0, OPT_NO_D, 0, 0 }, {"devices", 0, POPT_ARG_VAL, &preserve_devices, 1, 0, 0 }, {"no-devices", 0, POPT_ARG_VAL, &preserve_devices, 0, 0, 0 }, + {"copy-devices", 0, POPT_ARG_NONE, ©_devices, 0, 0, 0 }, {"specials", 0, POPT_ARG_VAL, &preserve_specials, 1, 0, 0 }, {"no-specials", 0, POPT_ARG_VAL, &preserve_specials, 0, 0, 0 }, {"links", 'l', POPT_ARG_VAL, &preserve_links, 1, 0, 0 }, @@ -2797,6 +2800,9 @@ void server_options(char **args, int *argc_p) else if (remove_source_files) args[ac++] = "--remove-sent-files"; + if (copy_devices) + args[ac++] = "--copy-devices"; + if (preallocate_files && am_sender) args[ac++] = "--preallocate"; diff --git a/proto.h b/proto.h index 1753f61..a21f39a 100644 --- a/proto.h +++ b/proto.h @@ -3,7 +3,9 @@ int allow_access(const char *addr, const char **host_ptr, int i); void free_acl(stat_x *sxp); int get_acl(const char *fname, stat_x *sxp); +void old_send_acl(stat_x *sxp, int f); void send_acl(int f, stat_x *sxp); +void old_recv_acl(struct file_struct *file, int f); void receive_acl(int f, struct file_struct *file); void cache_tmp_acl(struct file_struct *file, stat_x *sxp); void uncache_tmp_acls(void); diff --git a/rsync.1 b/rsync.1 index c11c02a..6cabd44 100644 --- a/rsync.1 +++ b/rsync.1 @@ -445,6 +445,7 @@ to the detailed description below for a complete description. \-o, \-\-owner preserve owner (super\-user only) \-g, \-\-group preserve group \-\-devices preserve device files (super\-user only) + \-\-copy-devices copy device contents as regular file \-\-specials preserve special files \-D same as \-\-devices \-\-specials \-t, \-\-times preserve modification times diff --git a/rsync.c b/rsync.c index a0b948d..ff761c0 100644 --- a/rsync.c +++ b/rsync.c @@ -33,6 +33,7 @@ extern int preserve_xattrs; extern int preserve_perms; extern int preserve_executability; extern int preserve_times; +extern int copy_devices; extern int am_root; extern int am_server; extern int am_daemon; @@ -410,7 +411,8 @@ int read_ndx_and_attrs(int f_in, int f_out, int *iflag_ptr, uchar *type_ptr, if (iflags & ITEM_TRANSFER) { int i = ndx - cur_flist->ndx_start; - if (i < 0 || !S_ISREG(cur_flist->files[i]->mode)) { + struct file_struct *file = cur_flist->files[i]; + if (i < 0 || !(S_ISREG(file->mode) || (copy_devices && IS_DEVICE(file->mode)))) { rprintf(FERROR, "received request to transfer non-regular file: %d [%s]\n", ndx, who_am_i()); diff --git a/sender.c b/sender.c index 03e4aad..9d33853 100644 --- a/sender.c +++ b/sender.c @@ -365,6 +365,20 @@ void send_files(int f_in, int f_out) exit_cleanup(RERR_FILEIO); } + /* On Matt's computer, st_size is falsely 0 for most devices. + * If this happens, try harder to determine the actual device size. */ + if (IS_DEVICE(st.st_mode) && st.st_size == 0) { + OFF_T off = lseek(fd, 0, SEEK_END); + if (off == (OFF_T) -1) + rsyserr(FERROR, errno, "failed to seek to end of %s to determine size", fname); + else { + st.st_size = off; + off = lseek(fd, 0, SEEK_SET); + if (off != 0) + rsyserr(FERROR, errno, "failed to seek back to beginning of %s to read it", fname); + } + } + if (st.st_size) { int32 read_size = MAX(s->blength * 3, MAX_MAP_SIZE); mbuf = map_file(fd, st.st_size, read_size, s->blength); diff --git a/xattrs.c b/xattrs.c index 5daef62..0c7db2e 100644 --- a/xattrs.c +++ b/xattrs.c @@ -22,6 +22,7 @@ #include "rsync.h" #include "ifuncs.h" #include "inums.h" +#include "io.h" #include "lib/sysxattrs.h" #ifdef SUPPORT_XATTRS @@ -38,6 +39,7 @@ extern int preserve_devices; extern int preserve_specials; extern int checksum_seed; extern int saw_xattr_filter; +extern int protocol_version; #define RSYNC_XAL_INITIAL 5 #define RSYNC_XAL_LIST_INITIAL 100 @@ -274,7 +276,7 @@ static int rsync_xal_get(const char *fname, item_list *xalp) if (!(ptr = get_xattr_data(fname, name, &datum_len, 0))) return -1; - if (datum_len > MAX_FULL_DATUM) { + if (datum_len > MAX_FULL_DATUM && protocol_version >= 30) { /* For large datums, we store a flag and a checksum. */ name_offset = 1 + MAX_DIGEST_LEN; sum_init(-1, checksum_seed); @@ -440,7 +442,7 @@ static int find_matching_xattr(const item_list *xalp) || rxas1[j].datum_len != rxas2[j].datum_len || strcmp(rxas1[j].name, rxas2[j].name)) break; - if (rxas1[j].datum_len > MAX_FULL_DATUM) { + if (rxas1[j].datum_len > MAX_FULL_DATUM && protocol_version >= 30) { if (memcmp(rxas1[j].datum + 1, rxas2[j].datum + 1, MAX_DIGEST_LEN) != 0) @@ -515,13 +517,22 @@ int send_xattr(int f, stat_x *sxp) { int ndx = find_matching_xattr(sxp->xattr); - /* Send 0 (-1 + 1) to indicate that literal xattr data follows. */ - write_varint(f, ndx + 1); + if (protocol_version < 30) { + if (ndx < 0) + write_byte(f, 'X'); + else { + write_byte(f, 'x'); + write_int(f, ndx); + } + } else { + /* Send 0 (-1 + 1) to indicate that literal xattr data follows. */ + write_varint(f, ndx + 1); + } if (ndx < 0) { rsync_xa *rxa; int count = sxp->xattr->count; - write_varint(f, count); + write_varint30(f, count); for (rxa = sxp->xattr->items; count--; rxa++) { size_t name_len = rxa->name_len; const char *name = rxa->name; @@ -540,8 +551,8 @@ int send_xattr(int f, stat_x *sxp) name_len += UPRE_LEN; } #endif - write_varint(f, name_len); - write_varint(f, rxa->datum_len); + write_varint30(f, name_len); + write_varint30(f, rxa->datum_len); #ifndef HAVE_LINUX_XATTRS if (name_len > rxa->name_len) { write_buf(f, USER_PREFIX, UPRE_LEN); @@ -549,7 +560,7 @@ int send_xattr(int f, stat_x *sxp) } #endif write_buf(f, name, name_len); - if (rxa->datum_len > MAX_FULL_DATUM) + if (rxa->datum_len > MAX_FULL_DATUM && protocol_version >= 30) write_buf(f, rxa->datum + 1, MAX_DIGEST_LEN); else write_bigbuf(f, rxa->datum, rxa->datum_len); @@ -600,7 +611,7 @@ int xattr_diff(struct file_struct *file, stat_x *sxp, int find_all) cmp = rec_cnt ? strcmp(snd_rxa->name, rec_rxa->name) : -1; if (cmp > 0) same = 0; - else if (snd_rxa->datum_len > MAX_FULL_DATUM) { + else if (snd_rxa->datum_len > MAX_FULL_DATUM && protocol_version >= 30) { same = cmp == 0 && snd_rxa->datum_len == rec_rxa->datum_len && memcmp(snd_rxa->datum + 1, rec_rxa->datum + 1, MAX_DIGEST_LEN) == 0; @@ -646,6 +657,9 @@ void send_xattr_request(const char *fname, struct file_struct *file, int f_out) int cnt, prior_req = 0; rsync_xa *rxa; + if (protocol_version < 30) + return; + glst += F_XATTR(file); lst = &glst->xa_items; @@ -705,6 +719,9 @@ int recv_xattr_request(struct file_struct *file, int f_in) rsync_xa *rxa; int rel_pos, cnt, num, got_xattr_data = 0; + if (protocol_version < 30) + return 0; + if (F_XATTR(file) < 0) { rprintf(FERROR, "recv_xattr_request: internal data error!\n"); exit_cleanup(RERR_PROTOCOL); @@ -791,7 +808,22 @@ void receive_xattr(int f, struct file_struct *file) #else int need_sort = 1; #endif - int ndx = read_varint(f); + int ndx; + + if (protocol_version >= 30) + ndx = read_varint(f); + else { + int tag = read_byte(f); + if (tag == 'x') + ndx = read_int(f) + 1; + else if (tag == 'X') + ndx = 0; + else { + rprintf(FERROR, "receive_xattr: unknown extended attribute" + " type tag (%02x) for %s\n", tag, f_name(file, NULL)); + exit_cleanup(RERR_STREAMIO); + } + } if (ndx < 0 || (size_t)ndx > rsync_xal_l.count) { rprintf(FERROR, "receive_xattr: xa index %d out of" @@ -804,7 +836,7 @@ void receive_xattr(int f, struct file_struct *file) return; } - if ((count = read_varint(f)) != 0) { + if ((count = read_varint30(f)) != 0) { (void)EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count); temp_xattr.count = 0; } @@ -812,9 +844,10 @@ void receive_xattr(int f, struct file_struct *file) for (num = 1; num <= count; num++) { char *ptr, *name; rsync_xa *rxa; - size_t name_len = read_varint(f); - size_t datum_len = read_varint(f); - size_t dget_len = datum_len > MAX_FULL_DATUM ? 1 + MAX_DIGEST_LEN : datum_len; + size_t name_len = read_varint30(f); + size_t datum_len = read_varint30(f); + size_t dget_len = datum_len > MAX_FULL_DATUM && protocol_version >= 30 + ? 1 + MAX_DIGEST_LEN : datum_len; size_t extra_len = MIGHT_NEED_RPRE ? RPRE_LEN : 0; if ((dget_len + extra_len < dget_len) || (dget_len + extra_len + name_len < dget_len + extra_len))