mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-05-25 07:15:35 -04:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd6058f3d4 | ||
|
|
9be3ba223c | ||
|
|
a261989cda | ||
|
|
7b5c3eb05e | ||
|
|
0feec72eee | ||
|
|
be8bd99aa4 | ||
|
|
355b8bcd73 | ||
|
|
d58e4c273c | ||
|
|
a217ad3095 | ||
|
|
3d6feada8a | ||
|
|
5f78da2025 | ||
|
|
a05e4fa512 | ||
|
|
2119a4c462 | ||
|
|
1d5a1da9f8 | ||
|
|
2e7d19945c | ||
|
|
5d2c5c4c73 | ||
|
|
8694312695 | ||
|
|
d9d6bc5278 | ||
|
|
ebed4c3af0 | ||
|
|
172875cf15 | ||
|
|
4d26e9e4f4 | ||
|
|
8f4455f296 |
34
NEWS
34
NEWS
@@ -1,7 +1,39 @@
|
||||
rsync 2.3.3 (26 Jan 2002)
|
||||
rsync 2.5.2 (26 Jan 2002)
|
||||
|
||||
SECURITY FIXES:
|
||||
|
||||
* Signedness security patch from Sebastian Krahmer
|
||||
<krahmer@suse.de> -- in some cases we were not sufficiently
|
||||
careful about reading integers from the network.
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
* Fix possible string mangling in log files.
|
||||
|
||||
* Fix for setting local address of outgoing sockets.
|
||||
|
||||
* Better handling of hardlinks and devices on platforms with
|
||||
64-bit dev_t or ino_t.
|
||||
|
||||
* Name resolution on machines supporting IPv6 is improved.
|
||||
|
||||
* Fix for device nodes. (dann frazier) (Debian #129135)
|
||||
|
||||
ENHANCEMENTS:
|
||||
|
||||
* With -v, rsync now shows the command used to initiate an ssh/rsh
|
||||
connection.
|
||||
|
||||
* --statistics now shows memory heap usage on platforms that
|
||||
support mallinfo().
|
||||
|
||||
* "The Ted T'so school of program optimization": make progress
|
||||
visible and people will think it's faster. (With --progress,
|
||||
rsync will show you how many files it has seen as it builds the
|
||||
file_list, giving some indication that it has not hung.)
|
||||
|
||||
* Improvements to batch mode support. This is still experimental
|
||||
but testing would be welcome. (Jos Backus)
|
||||
|
||||
* New --ignore-existing option, patch previously distributed with
|
||||
Vipul's Razor. (Debian #124286)
|
||||
|
||||
13
TODO
13
TODO
@@ -32,6 +32,16 @@ use chroot
|
||||
for people who want to generate the file list using a find(1)
|
||||
command or a script.
|
||||
|
||||
File list structure in memory
|
||||
|
||||
Rather than one big array, perhaps have a tree in memory mirroring
|
||||
the directory tree.
|
||||
|
||||
This might make sorting much faster! (I'm not sure it's a big CPU
|
||||
problem, mind you.)
|
||||
|
||||
It might also reduce memory use in storing repeated directory names
|
||||
-- again I'm not sure this is a problem.
|
||||
|
||||
Performance
|
||||
|
||||
@@ -94,9 +104,6 @@ Memory accounting
|
||||
not sure this makes sense with modern mallocs. At any rate it will
|
||||
make us allocate a huge amount of memory for large file lists.
|
||||
|
||||
We can try using the GNU/SVID/XPG mallinfo() function to get some
|
||||
heap statistics.
|
||||
|
||||
|
||||
Hard-link handling
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ char *client_name(int fd)
|
||||
client_sockaddr(fd, &ss, &ss_len);
|
||||
|
||||
if (!lookup_name(fd, &ss, ss_len, name_buf, sizeof name_buf, port_buf, sizeof port_buf))
|
||||
check_name(fd, &ss, ss_len, name_buf, port_buf);
|
||||
check_name(fd, &ss, name_buf, port_buf);
|
||||
|
||||
return name_buf;
|
||||
}
|
||||
@@ -229,7 +229,6 @@ int compare_addrinfo_sockaddr(const struct addrinfo *ai,
|
||||
**/
|
||||
int check_name(int fd,
|
||||
const struct sockaddr_storage *ss,
|
||||
socklen_t ss_len,
|
||||
char *name_buf,
|
||||
const char *port_buf)
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ AC_CONFIG_SRCDIR([byteorder.h])
|
||||
AC_CONFIG_HEADER(config.h)
|
||||
AC_PREREQ(2.52)
|
||||
|
||||
RSYNC_VERSION=2.5.2pre3
|
||||
RSYNC_VERSION=2.5.2
|
||||
AC_SUBST(RSYNC_VERSION)
|
||||
AC_MSG_NOTICE([Configuring rsync $RSYNC_VERSION])
|
||||
|
||||
|
||||
151
exclude.c
151
exclude.c
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Copyright (C) Andrew Tridgell 1996
|
||||
Copyright (C) Paul Mackerras 1996
|
||||
/* -*- c-file-style: "linux" -*-
|
||||
|
||||
Copyright (C) 1996-2001 by Andrew Tridgell <tridge@samba.org>
|
||||
Copyright (C) 1996 by Paul Mackerras
|
||||
|
||||
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
|
||||
@@ -20,6 +21,8 @@
|
||||
/* a lot of this stuff was originally derived from GNU tar, although
|
||||
it has now changed so much that it is hard to tell :) */
|
||||
|
||||
/* include/exclude cluestick added by Martin Pool <mbp@samba.org> */
|
||||
|
||||
#include "rsync.h"
|
||||
|
||||
extern int verbose;
|
||||
@@ -27,52 +30,8 @@ extern int delete_mode;
|
||||
|
||||
static struct exclude_struct **exclude_list;
|
||||
|
||||
/*
|
||||
* Optimization for special case when all included files are explicitly
|
||||
* listed without wildcards in the "exclude" list followed by a "- *"
|
||||
* to exclude the rest.
|
||||
* Contributed by Dave Dykstra <dwd@bell-labs.com>
|
||||
*/
|
||||
static int only_included_files = 1;
|
||||
static struct exclude_struct *exclude_the_rest;
|
||||
|
||||
int send_included_file_names(int f,struct file_list *flist)
|
||||
{
|
||||
struct exclude_struct *ex, **ex_list;
|
||||
int n;
|
||||
char *p;
|
||||
|
||||
if (!only_included_files || (exclude_the_rest == NULL) || delete_mode)
|
||||
return 0;
|
||||
|
||||
if (verbose > 1) {
|
||||
rprintf(FINFO,"(using include-only optimization) ");
|
||||
}
|
||||
|
||||
/* set exclude_list to NULL temporarily so check_exclude */
|
||||
/* will always return true */
|
||||
ex_list = exclude_list;
|
||||
exclude_list = NULL;
|
||||
for (n=0; (ex = ex_list[n]) != NULL; n++) {
|
||||
if (ex == exclude_the_rest)
|
||||
break;
|
||||
p = ex->pattern;
|
||||
while (*p == '/') {
|
||||
/* skip the allowed beginning slashes */
|
||||
p++;
|
||||
}
|
||||
/* silently skip files that don't exist to
|
||||
be more like non-optimized case */
|
||||
if (access(p,0) == 0)
|
||||
send_file_name(f,flist,p,0,0);
|
||||
}
|
||||
exclude_list = ex_list;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* build an exclude structure given a exclude pattern */
|
||||
static struct exclude_struct *make_exclude(char *pattern, int include)
|
||||
static struct exclude_struct *make_exclude(const char *pattern, int include)
|
||||
{
|
||||
struct exclude_struct *ret;
|
||||
|
||||
@@ -95,15 +54,18 @@ static struct exclude_struct *make_exclude(char *pattern, int include)
|
||||
if (!ret->pattern) out_of_memory("make_exclude");
|
||||
|
||||
if (strpbrk(pattern, "*[?")) {
|
||||
if (!ret->include && (*pattern == '*') && (*(pattern+1) == '\0')) {
|
||||
exclude_the_rest = ret;
|
||||
} else {
|
||||
only_included_files = 0;
|
||||
}
|
||||
ret->regular_exp = 1;
|
||||
ret->fnmatch_flags = strstr(pattern, "**") ? 0 : FNM_PATHNAME;
|
||||
} else if (!ret->include) {
|
||||
only_included_files = 0;
|
||||
ret->fnmatch_flags = FNM_PATHNAME;
|
||||
if (strstr(pattern, "**")) {
|
||||
static int tested;
|
||||
if (!tested) {
|
||||
tested = 1;
|
||||
if (fnmatch("a/b/*", "a/b/c/d", FNM_PATHNAME)==0) {
|
||||
rprintf(FERROR,"WARNING: fnmatch FNM_PATHNAME is broken on your system\n");
|
||||
}
|
||||
}
|
||||
ret->fnmatch_flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen(pattern) > 1 && pattern[strlen(pattern)-1] == '/') {
|
||||
@@ -125,8 +87,8 @@ static void free_exclude(struct exclude_struct *ex)
|
||||
free(ex);
|
||||
}
|
||||
|
||||
static int check_one_exclude(char *name,struct exclude_struct *ex,
|
||||
STRUCT_STAT *st)
|
||||
static int check_one_exclude(char *name, struct exclude_struct *ex,
|
||||
STRUCT_STAT *st)
|
||||
{
|
||||
char *p;
|
||||
int match_start=0;
|
||||
@@ -145,47 +107,79 @@ static int check_one_exclude(char *name,struct exclude_struct *ex,
|
||||
}
|
||||
|
||||
if (ex->regular_exp) {
|
||||
if (fnmatch(pattern, name, ex->fnmatch_flags) == 0)
|
||||
if (fnmatch(pattern, name, ex->fnmatch_flags) == 0) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
int l1 = strlen(name);
|
||||
int l2 = strlen(pattern);
|
||||
if (l2 <= l1 &&
|
||||
strcmp(name+(l1-l2),pattern) == 0 &&
|
||||
(l1==l2 || (!match_start && name[l1-(l2+1)] == '/')))
|
||||
(l1==l2 || (!match_start && name[l1-(l2+1)] == '/'))) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int check_exclude(char *name,struct exclude_struct **local_exclude_list,
|
||||
static void report_exclude_result(char const *name,
|
||||
struct exclude_struct const *ent,
|
||||
STRUCT_STAT const *st)
|
||||
{
|
||||
/* If a trailing slash is present to match only directories,
|
||||
* then it is stripped out by make_exclude. So as a special
|
||||
* case we add it back in here. */
|
||||
|
||||
if (verbose >= 2)
|
||||
rprintf(FINFO, "%s %s %s because of pattern %s%s\n",
|
||||
ent->include ? "including" : "excluding",
|
||||
S_ISDIR(st->st_mode) ? "directory" : "file",
|
||||
name, ent->pattern,
|
||||
ent->directory ? "/" : "");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return true if file NAME is defined to be excluded by either
|
||||
* LOCAL_EXCLUDE_LIST or the globals EXCLUDE_LIST.
|
||||
*/
|
||||
int check_exclude(char *name, struct exclude_struct **local_exclude_list,
|
||||
STRUCT_STAT *st)
|
||||
{
|
||||
int n;
|
||||
struct exclude_struct *ent;
|
||||
|
||||
if (name && (name[0] == '.') && !name[1])
|
||||
/* never exclude '.', even if somebody does --exclude '*' */
|
||||
return 0;
|
||||
|
||||
if (exclude_list) {
|
||||
for (n=0; exclude_list[n]; n++)
|
||||
if (check_one_exclude(name,exclude_list[n],st))
|
||||
return !exclude_list[n]->include;
|
||||
for (n=0; exclude_list[n]; n++) {
|
||||
ent = exclude_list[n];
|
||||
if (check_one_exclude(name, ent, st)) {
|
||||
report_exclude_result(name, ent, st);
|
||||
return !ent->include;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (local_exclude_list) {
|
||||
for (n=0; local_exclude_list[n]; n++)
|
||||
if (check_one_exclude(name,local_exclude_list[n],st))
|
||||
return !local_exclude_list[n]->include;
|
||||
for (n=0; local_exclude_list[n]; n++) {
|
||||
ent = local_exclude_list[n];
|
||||
if (check_one_exclude(name, ent, st)) {
|
||||
report_exclude_result(name, ent, st);
|
||||
return !ent->include;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void add_exclude_list(char *pattern,struct exclude_struct ***list, int include)
|
||||
void add_exclude_list(const char *pattern, struct exclude_struct ***list, int include)
|
||||
{
|
||||
int len=0;
|
||||
if (list && *list)
|
||||
@@ -199,8 +193,6 @@ void add_exclude_list(char *pattern,struct exclude_struct ***list, int include)
|
||||
}
|
||||
free((*list));
|
||||
*list = NULL;
|
||||
only_included_files = 1;
|
||||
exclude_the_rest = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -215,12 +207,12 @@ void add_exclude_list(char *pattern,struct exclude_struct ***list, int include)
|
||||
(*list)[len+1] = NULL;
|
||||
}
|
||||
|
||||
void add_exclude(char *pattern, int include)
|
||||
void add_exclude(const char *pattern, int include)
|
||||
{
|
||||
add_exclude_list(pattern,&exclude_list, include);
|
||||
}
|
||||
|
||||
struct exclude_struct **make_exclude_list(char *fname,
|
||||
struct exclude_struct **make_exclude_list(const char *fname,
|
||||
struct exclude_struct **list1,
|
||||
int fatal, int include)
|
||||
{
|
||||
@@ -229,7 +221,10 @@ struct exclude_struct **make_exclude_list(char *fname,
|
||||
char line[MAXPATHLEN];
|
||||
if (!f) {
|
||||
if (fatal) {
|
||||
rprintf(FERROR,"%s : %s\n",fname,strerror(errno));
|
||||
rsyserr(FERROR, errno,
|
||||
"failed to open %s file %s",
|
||||
include ? "include" : "exclude",
|
||||
fname);
|
||||
exit_cleanup(RERR_FILEIO);
|
||||
}
|
||||
return list;
|
||||
@@ -251,7 +246,7 @@ struct exclude_struct **make_exclude_list(char *fname,
|
||||
}
|
||||
|
||||
|
||||
void add_exclude_file(char *fname,int fatal,int include)
|
||||
void add_exclude_file(const char *fname, int fatal, int include)
|
||||
{
|
||||
if (!fname || !*fname) return;
|
||||
|
||||
@@ -263,6 +258,12 @@ void send_exclude_list(int f)
|
||||
{
|
||||
int i;
|
||||
extern int remote_version;
|
||||
extern int list_only, recurse;
|
||||
|
||||
/* this is a complete hack - blame Rusty */
|
||||
if (list_only && !recurse) {
|
||||
add_exclude("/*/*", 0);
|
||||
}
|
||||
|
||||
if (!exclude_list) {
|
||||
write_int(f,0);
|
||||
@@ -398,7 +399,7 @@ void add_cvs_excludes(void)
|
||||
add_exclude(cvs_ignore_list[i], 0);
|
||||
|
||||
if ((p=getenv("HOME")) && strlen(p) < (MAXPATHLEN-12)) {
|
||||
slprintf(fname,sizeof(fname), "%s/.cvsignore",p);
|
||||
snprintf(fname,sizeof(fname), "%s/.cvsignore",p);
|
||||
add_exclude_file(fname,0,0);
|
||||
}
|
||||
|
||||
|
||||
10
fileio.c
10
fileio.c
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright (C) Andrew Tridgell 1998
|
||||
Copyright (C) 2002 by Martin Pool
|
||||
|
||||
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
|
||||
@@ -38,7 +39,7 @@ int sparse_end(int f)
|
||||
|
||||
static int write_sparse(int f,char *buf,size_t len)
|
||||
{
|
||||
int l1=0,l2=0;
|
||||
size_t l1=0, l2=0;
|
||||
int ret;
|
||||
|
||||
for (l1=0;l1<len && buf[l1]==0;l1++) ;
|
||||
@@ -56,10 +57,11 @@ static int write_sparse(int f,char *buf,size_t len)
|
||||
if (l1 == len)
|
||||
return len;
|
||||
|
||||
if ((ret=write(f,buf+l1,len-(l1+l2))) != len-(l1+l2)) {
|
||||
if (ret == -1 || ret == 0) return ret;
|
||||
ret = write(f, buf + l1, len - (l1+l2));
|
||||
if (ret == -1 || ret == 0)
|
||||
return ret;
|
||||
else if (ret != (int) (len - (l1+l2)))
|
||||
return (l1+ret);
|
||||
}
|
||||
|
||||
if (l2 > 0)
|
||||
do_lseek(f,l2,SEEK_CUR);
|
||||
|
||||
@@ -31,6 +31,7 @@ extern int am_root;
|
||||
extern int preserve_devices;
|
||||
extern int preserve_hard_links;
|
||||
extern int update_only;
|
||||
extern int opt_ignore_existing;
|
||||
extern int whole_file;
|
||||
extern int block_size;
|
||||
extern int csum_length;
|
||||
@@ -383,6 +384,12 @@ void recv_generator(char *fname,struct file_list *flist,int i,int f_out)
|
||||
return;
|
||||
}
|
||||
|
||||
if (opt_ignore_existing && fnamecmp == fname) {
|
||||
if (verbose > 1)
|
||||
rprintf(FINFO,"%s exists\n",fname);
|
||||
return;
|
||||
}
|
||||
|
||||
if (update_only && cmp_modtime(st.st_mtime,file->modtime)>0 && fnamecmp == fname) {
|
||||
if (verbose > 1)
|
||||
rprintf(FINFO,"%s is newer\n",fname);
|
||||
|
||||
444
io.c
444
io.c
@@ -1,6 +1,8 @@
|
||||
/*
|
||||
Copyright (C) Andrew Tridgell 1996
|
||||
/* -*- c-file-style: "linux" -*-
|
||||
|
||||
Copyright (C) 1996-2001 by Andrew Tridgell
|
||||
Copyright (C) Paul Mackerras 1996
|
||||
Copyright (C) 2001 by Martin Pool <mbp@samba.org>
|
||||
|
||||
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
|
||||
@@ -18,7 +20,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
Utilities used in rsync
|
||||
socket and pipe IO utilities used in rsync
|
||||
|
||||
tridge, June 1996
|
||||
*/
|
||||
@@ -32,21 +34,29 @@ static int io_multiplexing_in;
|
||||
static int multiplex_in_fd;
|
||||
static int multiplex_out_fd;
|
||||
static time_t last_io;
|
||||
static int eof_error=1;
|
||||
static int no_flush;
|
||||
|
||||
extern int bwlimit;
|
||||
extern int verbose;
|
||||
extern int io_timeout;
|
||||
extern struct stats stats;
|
||||
|
||||
static int buffer_f_in = -1;
|
||||
|
||||
void setup_readbuffer(int f_in)
|
||||
{
|
||||
buffer_f_in = f_in;
|
||||
}
|
||||
/** Ignore EOF errors while reading a module listing if the remote
|
||||
version is 24 or less. */
|
||||
int kludge_around_eof = False;
|
||||
|
||||
|
||||
static int io_error_fd = -1;
|
||||
|
||||
static void read_loop(int fd, char *buf, size_t len);
|
||||
|
||||
static void check_timeout(void)
|
||||
{
|
||||
extern int am_server, am_daemon;
|
||||
time_t t;
|
||||
|
||||
err_list_push();
|
||||
|
||||
if (!io_timeout) return;
|
||||
|
||||
@@ -58,45 +68,146 @@ static void check_timeout(void)
|
||||
t = time(NULL);
|
||||
|
||||
if (last_io && io_timeout && (t-last_io) >= io_timeout) {
|
||||
rprintf(FERROR,"io timeout after %d second - exiting\n",
|
||||
(int)(t-last_io));
|
||||
if (!am_server && !am_daemon) {
|
||||
rprintf(FERROR,"io timeout after %d seconds - exiting\n",
|
||||
(int)(t-last_io));
|
||||
}
|
||||
exit_cleanup(RERR_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/* setup the fd used to propogate errors */
|
||||
void io_set_error_fd(int fd)
|
||||
{
|
||||
io_error_fd = fd;
|
||||
}
|
||||
|
||||
static char *read_buffer;
|
||||
static char *read_buffer_p;
|
||||
static int read_buffer_len;
|
||||
static int read_buffer_size;
|
||||
static int no_flush;
|
||||
static int no_flush_read;
|
||||
/* read some data from the error fd and write it to the write log code */
|
||||
static void read_error_fd(void)
|
||||
{
|
||||
char buf[200];
|
||||
size_t n;
|
||||
int fd = io_error_fd;
|
||||
int tag, len;
|
||||
|
||||
/* read from a socket with IO timeout. return the number of
|
||||
bytes read. If no bytes can be read then exit, never return
|
||||
a number <= 0 */
|
||||
static int read_timeout(int fd, char *buf, size_t len)
|
||||
/* io_error_fd is temporarily disabled -- is this meant to
|
||||
* prevent indefinite recursion? */
|
||||
io_error_fd = -1;
|
||||
|
||||
read_loop(fd, buf, 4);
|
||||
tag = IVAL(buf, 0);
|
||||
|
||||
len = tag & 0xFFFFFF;
|
||||
tag = tag >> 24;
|
||||
tag -= MPLEX_BASE;
|
||||
|
||||
while (len) {
|
||||
n = len;
|
||||
if (n > (sizeof(buf)-1))
|
||||
n = sizeof(buf)-1;
|
||||
read_loop(fd, buf, n);
|
||||
rwrite((enum logcode)tag, buf, n);
|
||||
len -= n;
|
||||
}
|
||||
|
||||
io_error_fd = fd;
|
||||
}
|
||||
|
||||
|
||||
static void whine_about_eof (void)
|
||||
{
|
||||
/**
|
||||
It's almost always an error to get an EOF when we're trying
|
||||
to read from the network, because the protocol is
|
||||
self-terminating.
|
||||
|
||||
However, there is one unfortunate cases where it is not,
|
||||
which is rsync <2.4.6 sending a list of modules on a
|
||||
server, since the list is terminated by closing the socket.
|
||||
So, for the section of the program where that is a problem
|
||||
(start_socket_client), kludge_around_eof is True and we
|
||||
just exit.
|
||||
*/
|
||||
|
||||
if (kludge_around_eof)
|
||||
exit_cleanup (0);
|
||||
else {
|
||||
rprintf (FERROR,
|
||||
"%s: connection unexpectedly closed "
|
||||
"(%.0f bytes read so far)\n",
|
||||
RSYNC_NAME, (double)stats.total_read);
|
||||
|
||||
exit_cleanup (RERR_STREAMIO);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void die_from_readerr (int err)
|
||||
{
|
||||
/* this prevents us trying to write errors on a dead socket */
|
||||
io_multiplexing_close();
|
||||
|
||||
rprintf(FERROR, "%s: read error: %s\n",
|
||||
RSYNC_NAME, strerror (err));
|
||||
exit_cleanup(RERR_STREAMIO);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
* Read from a socket with IO timeout. return the number of bytes
|
||||
* read. If no bytes can be read then exit, never return a number <= 0.
|
||||
*
|
||||
* TODO: If the remote shell connection fails, then current versions
|
||||
* actually report an "unexpected EOF" error here. Since it's a
|
||||
* fairly common mistake to try to use rsh when ssh is required, we
|
||||
* should trap that: if we fail to read any data at all, we should
|
||||
* give a better explanation. We can tell whether the connection has
|
||||
* started by looking e.g. at whether the remote version is known yet.
|
||||
*/
|
||||
static int read_timeout (int fd, char *buf, size_t len)
|
||||
{
|
||||
int n, ret=0;
|
||||
|
||||
no_flush_read++;
|
||||
io_flush();
|
||||
no_flush_read--;
|
||||
|
||||
while (ret == 0) {
|
||||
/* until we manage to read *something* */
|
||||
fd_set fds;
|
||||
struct timeval tv;
|
||||
int fd_count = fd+1;
|
||||
int count;
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fd, &fds);
|
||||
if (io_error_fd != -1) {
|
||||
FD_SET(io_error_fd, &fds);
|
||||
if (io_error_fd > fd) fd_count = io_error_fd+1;
|
||||
}
|
||||
|
||||
tv.tv_sec = io_timeout?io_timeout:SELECT_TIMEOUT;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
if (select(fd+1, &fds, NULL, NULL, &tv) != 1) {
|
||||
errno = 0;
|
||||
|
||||
count = select(fd_count, &fds, NULL, NULL, &tv);
|
||||
|
||||
if (count == 0) {
|
||||
check_timeout();
|
||||
}
|
||||
|
||||
if (count <= 0) {
|
||||
if (errno == EBADF) {
|
||||
exit_cleanup(RERR_SOCKETIO);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (io_error_fd != -1 && FD_ISSET(io_error_fd, &fds)) {
|
||||
read_error_fd();
|
||||
}
|
||||
|
||||
if (!FD_ISSET(fd, &fds)) continue;
|
||||
|
||||
n = read(fd, buf, len);
|
||||
|
||||
if (n > 0) {
|
||||
@@ -106,38 +217,27 @@ static int read_timeout(int fd, char *buf, size_t len)
|
||||
if (io_timeout)
|
||||
last_io = time(NULL);
|
||||
continue;
|
||||
} else if (n == 0) {
|
||||
whine_about_eof ();
|
||||
return -1; /* doesn't return */
|
||||
} else if (n == -1) {
|
||||
if (errno == EINTR || errno == EWOULDBLOCK ||
|
||||
errno == EAGAIN)
|
||||
continue;
|
||||
else
|
||||
die_from_readerr (errno);
|
||||
}
|
||||
|
||||
if (n == -1 && errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n == -1 &&
|
||||
(errno == EAGAIN || errno == EWOULDBLOCK)) {
|
||||
/* this shouldn't happen, if it does then
|
||||
sleep for a short time to prevent us
|
||||
chewing too much CPU */
|
||||
u_sleep(100);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
if (eof_error) {
|
||||
rprintf(FERROR,"unexpected EOF in read_timeout\n");
|
||||
}
|
||||
exit_cleanup(RERR_STREAMIO);
|
||||
}
|
||||
|
||||
rprintf(FERROR,"read error: %s\n", strerror(errno));
|
||||
exit_cleanup(RERR_STREAMIO);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* continue trying to read len bytes - don't return until len
|
||||
has been read */
|
||||
static void read_loop(int fd, char *buf, size_t len)
|
||||
|
||||
|
||||
|
||||
/*! Continue trying to read len bytes - don't return until len has
|
||||
been read. */
|
||||
static void read_loop (int fd, char *buf, size_t len)
|
||||
{
|
||||
while (len) {
|
||||
int n = read_timeout(fd, buf, len);
|
||||
@@ -147,17 +247,20 @@ static void read_loop(int fd, char *buf, size_t len)
|
||||
}
|
||||
}
|
||||
|
||||
/* read from the file descriptor handling multiplexing -
|
||||
return number of bytes read
|
||||
never return <= 0 */
|
||||
|
||||
/**
|
||||
* Read from the file descriptor handling multiplexing - return number
|
||||
* of bytes read.
|
||||
*
|
||||
* Never returns <= 0.
|
||||
*/
|
||||
static int read_unbuffered(int fd, char *buf, size_t len)
|
||||
{
|
||||
static int remaining;
|
||||
char ibuf[4];
|
||||
int tag, ret=0;
|
||||
static size_t remaining;
|
||||
int tag, ret = 0;
|
||||
char line[1024];
|
||||
|
||||
if (!io_multiplexing_in || fd != multiplex_in_fd)
|
||||
if (!io_multiplexing_in || fd != multiplex_in_fd)
|
||||
return read_timeout(fd, buf, len);
|
||||
|
||||
while (ret == 0) {
|
||||
@@ -169,23 +272,24 @@ static int read_unbuffered(int fd, char *buf, size_t len)
|
||||
continue;
|
||||
}
|
||||
|
||||
read_loop(fd, ibuf, 4);
|
||||
tag = IVAL(ibuf, 0);
|
||||
read_loop(fd, line, 4);
|
||||
tag = IVAL(line, 0);
|
||||
|
||||
remaining = tag & 0xFFFFFF;
|
||||
tag = tag >> 24;
|
||||
|
||||
if (tag == MPLEX_BASE) continue;
|
||||
if (tag == MPLEX_BASE)
|
||||
continue;
|
||||
|
||||
tag -= MPLEX_BASE;
|
||||
|
||||
if (tag != FERROR && tag != FINFO) {
|
||||
rprintf(FERROR,"unexpected tag %d\n", tag);
|
||||
rprintf(FERROR, "unexpected tag %d\n", tag);
|
||||
exit_cleanup(RERR_STREAMIO);
|
||||
}
|
||||
|
||||
if (remaining > sizeof(line)-1) {
|
||||
rprintf(FERROR,"multiplexing overflow %d\n\n",
|
||||
if (remaining > sizeof(line) - 1) {
|
||||
rprintf(FERROR, "multiplexing overflow %d\n\n",
|
||||
remaining);
|
||||
exit_cleanup(RERR_STREAMIO);
|
||||
}
|
||||
@@ -193,7 +297,7 @@ static int read_unbuffered(int fd, char *buf, size_t len)
|
||||
read_loop(fd, line, remaining);
|
||||
line[remaining] = 0;
|
||||
|
||||
rprintf(tag,"%s", line);
|
||||
rprintf((enum logcode) tag, "%s", line);
|
||||
remaining = 0;
|
||||
}
|
||||
|
||||
@@ -202,65 +306,17 @@ static int read_unbuffered(int fd, char *buf, size_t len)
|
||||
|
||||
|
||||
|
||||
/* This function was added to overcome a deadlock problem when using
|
||||
* ssh. It looks like we can't allow our receive queue to get full or
|
||||
* ssh will clag up. Uggh. */
|
||||
static void read_check(int f)
|
||||
{
|
||||
int n = 8192;
|
||||
|
||||
if (f == -1) return;
|
||||
|
||||
if (read_buffer_len == 0) {
|
||||
read_buffer_p = read_buffer;
|
||||
}
|
||||
|
||||
if (n > MAX_READ_BUFFER/4)
|
||||
n = MAX_READ_BUFFER/4;
|
||||
|
||||
if (read_buffer_p != read_buffer) {
|
||||
memmove(read_buffer,read_buffer_p,read_buffer_len);
|
||||
read_buffer_p = read_buffer;
|
||||
}
|
||||
|
||||
if (n > (read_buffer_size - read_buffer_len)) {
|
||||
read_buffer_size += n;
|
||||
read_buffer = (char *)Realloc(read_buffer,read_buffer_size);
|
||||
if (!read_buffer) out_of_memory("read check");
|
||||
read_buffer_p = read_buffer;
|
||||
}
|
||||
|
||||
n = read_unbuffered(f,read_buffer+read_buffer_len,n);
|
||||
read_buffer_len += n;
|
||||
}
|
||||
|
||||
|
||||
/* do a buffered read from fd. don't return until all N bytes
|
||||
have been read. If all N can't be read then exit with an error */
|
||||
static void readfd(int fd,char *buffer,size_t N)
|
||||
static void readfd (int fd, char *buffer, size_t N)
|
||||
{
|
||||
int ret;
|
||||
int total=0;
|
||||
|
||||
if ((read_buffer_len < N) && (N < 1024)) {
|
||||
read_check(buffer_f_in);
|
||||
}
|
||||
size_t total=0;
|
||||
|
||||
while (total < N) {
|
||||
if (read_buffer_len > 0 && buffer_f_in == fd) {
|
||||
ret = MIN(read_buffer_len,N-total);
|
||||
memcpy(buffer+total,read_buffer_p,ret);
|
||||
read_buffer_p += ret;
|
||||
read_buffer_len -= ret;
|
||||
total += ret;
|
||||
continue;
|
||||
}
|
||||
|
||||
no_flush_read++;
|
||||
io_flush();
|
||||
no_flush_read--;
|
||||
|
||||
ret = read_unbuffered(fd,buffer + total,N-total);
|
||||
ret = read_unbuffered (fd, buffer + total, N-total);
|
||||
total += ret;
|
||||
}
|
||||
|
||||
@@ -310,30 +366,26 @@ void read_buf(int f,char *buf,size_t len)
|
||||
|
||||
void read_sbuf(int f,char *buf,size_t len)
|
||||
{
|
||||
read_buf(f,buf,len);
|
||||
read_buf (f,buf,len);
|
||||
buf[len] = 0;
|
||||
}
|
||||
|
||||
unsigned char read_byte(int f)
|
||||
{
|
||||
unsigned char c;
|
||||
read_buf(f,(char *)&c,1);
|
||||
read_buf (f, (char *)&c, 1);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* write len bytes to fd, possibly reading from buffer_f_in if set
|
||||
in order to unclog the pipe. don't return until all len
|
||||
bytes have been written */
|
||||
/* write len bytes to fd */
|
||||
static void writefd_unbuffered(int fd,char *buf,size_t len)
|
||||
{
|
||||
int total = 0;
|
||||
size_t total = 0;
|
||||
fd_set w_fds, r_fds;
|
||||
int fd_count, count;
|
||||
struct timeval tv;
|
||||
int reading=0;
|
||||
int blocked=0;
|
||||
|
||||
err_list_push();
|
||||
|
||||
no_flush++;
|
||||
|
||||
@@ -341,55 +393,75 @@ static void writefd_unbuffered(int fd,char *buf,size_t len)
|
||||
FD_ZERO(&w_fds);
|
||||
FD_ZERO(&r_fds);
|
||||
FD_SET(fd,&w_fds);
|
||||
fd_count = fd+1;
|
||||
fd_count = fd;
|
||||
|
||||
if (!no_flush_read) {
|
||||
reading = (buffer_f_in != -1);
|
||||
}
|
||||
|
||||
if (reading) {
|
||||
FD_SET(buffer_f_in,&r_fds);
|
||||
if (buffer_f_in > fd)
|
||||
fd_count = buffer_f_in+1;
|
||||
if (io_error_fd != -1) {
|
||||
FD_SET(io_error_fd,&r_fds);
|
||||
if (io_error_fd > fd_count)
|
||||
fd_count = io_error_fd;
|
||||
}
|
||||
|
||||
tv.tv_sec = io_timeout?io_timeout:SELECT_TIMEOUT;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
count = select(fd_count,
|
||||
reading?&r_fds:NULL,
|
||||
errno = 0;
|
||||
|
||||
count = select(fd_count+1,
|
||||
io_error_fd != -1?&r_fds:NULL,
|
||||
&w_fds,NULL,
|
||||
&tv);
|
||||
|
||||
if (count <= 0) {
|
||||
if (count == 0) {
|
||||
check_timeout();
|
||||
}
|
||||
|
||||
if (count <= 0) {
|
||||
if (errno == EBADF) {
|
||||
exit_cleanup(RERR_SOCKETIO);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reading && FD_ISSET(buffer_f_in, &r_fds)) {
|
||||
read_check(buffer_f_in);
|
||||
if (io_error_fd != -1 && FD_ISSET(io_error_fd, &r_fds)) {
|
||||
read_error_fd();
|
||||
}
|
||||
|
||||
if (FD_ISSET(fd, &w_fds)) {
|
||||
int n = (len-total)>>blocked;
|
||||
int ret = write(fd,buf+total,n?n:1);
|
||||
int ret;
|
||||
size_t n = len-total;
|
||||
ret = write(fd,buf+total,n);
|
||||
|
||||
if (ret == -1 && errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret == -1 &&
|
||||
(errno == EAGAIN || errno == EWOULDBLOCK)) {
|
||||
blocked++;
|
||||
(errno == EWOULDBLOCK || errno == EAGAIN)) {
|
||||
msleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret <= 0) {
|
||||
rprintf(FERROR,"erroring writing %d bytes - exiting\n", len);
|
||||
rprintf(FERROR,
|
||||
"error writing %d unbuffered bytes"
|
||||
" - exiting: %s\n", len,
|
||||
strerror(errno));
|
||||
exit_cleanup(RERR_STREAMIO);
|
||||
}
|
||||
|
||||
blocked = 0;
|
||||
/* Sleep after writing to limit I/O bandwidth */
|
||||
if (bwlimit)
|
||||
{
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = ret * 1000 / bwlimit;
|
||||
while (tv.tv_usec > 1000000)
|
||||
{
|
||||
tv.tv_sec++;
|
||||
tv.tv_usec -= 1000000;
|
||||
}
|
||||
select(0, NULL, NULL, NULL, &tv);
|
||||
}
|
||||
|
||||
total += ret;
|
||||
|
||||
if (io_timeout)
|
||||
@@ -408,33 +480,58 @@ void io_start_buffering(int fd)
|
||||
{
|
||||
if (io_buffer) return;
|
||||
multiplex_out_fd = fd;
|
||||
io_buffer = (char *)malloc(IO_BUFFER_SIZE+4);
|
||||
io_buffer = (char *)malloc(IO_BUFFER_SIZE);
|
||||
if (!io_buffer) out_of_memory("writefd");
|
||||
io_buffer_count = 0;
|
||||
|
||||
/* leave room for the multiplex header in case it's needed */
|
||||
io_buffer += 4;
|
||||
}
|
||||
|
||||
/* write an message to a multiplexed stream. If this fails then rsync
|
||||
exits */
|
||||
static void mplex_write(int fd, enum logcode code, char *buf, size_t len)
|
||||
{
|
||||
char buffer[4096];
|
||||
size_t n = len;
|
||||
|
||||
SIVAL(buffer, 0, ((MPLEX_BASE + (int)code)<<24) + len);
|
||||
|
||||
if (n > (sizeof(buffer)-4)) {
|
||||
n = sizeof(buffer)-4;
|
||||
}
|
||||
|
||||
memcpy(&buffer[4], buf, n);
|
||||
writefd_unbuffered(fd, buffer, n+4);
|
||||
|
||||
len -= n;
|
||||
buf += n;
|
||||
|
||||
if (len) {
|
||||
writefd_unbuffered(fd, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void io_flush(void)
|
||||
{
|
||||
int fd = multiplex_out_fd;
|
||||
|
||||
err_list_push();
|
||||
|
||||
if (!io_buffer_count || no_flush) return;
|
||||
|
||||
if (io_multiplexing_out) {
|
||||
SIVAL(io_buffer-4, 0, (MPLEX_BASE<<24) + io_buffer_count);
|
||||
writefd_unbuffered(fd, io_buffer-4, io_buffer_count+4);
|
||||
mplex_write(fd, FNONE, io_buffer, io_buffer_count);
|
||||
} else {
|
||||
writefd_unbuffered(fd, io_buffer, io_buffer_count);
|
||||
}
|
||||
io_buffer_count = 0;
|
||||
}
|
||||
|
||||
void io_end_buffering(int fd)
|
||||
|
||||
void io_end_buffering(void)
|
||||
{
|
||||
io_flush();
|
||||
if (!io_multiplexing_out) {
|
||||
free(io_buffer-4);
|
||||
free(io_buffer);
|
||||
io_buffer = NULL;
|
||||
}
|
||||
}
|
||||
@@ -443,13 +540,15 @@ static void writefd(int fd,char *buf,size_t len)
|
||||
{
|
||||
stats.total_written += len;
|
||||
|
||||
if (!io_buffer) {
|
||||
err_list_push();
|
||||
|
||||
if (!io_buffer || fd != multiplex_out_fd) {
|
||||
writefd_unbuffered(fd, buf, len);
|
||||
return;
|
||||
}
|
||||
|
||||
while (len) {
|
||||
int n = MIN(len, IO_BUFFER_SIZE-io_buffer_count);
|
||||
int n = MIN((int) len, IO_BUFFER_SIZE-io_buffer_count);
|
||||
if (n > 0) {
|
||||
memcpy(io_buffer+io_buffer_count, buf, n);
|
||||
buf += n;
|
||||
@@ -469,6 +568,11 @@ void write_int(int f,int32 x)
|
||||
writefd(f,b,4);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Note: int64 may actually be a 32-bit type if ./configure couldn't find any
|
||||
* 64-bit types on this platform.
|
||||
*/
|
||||
void write_longint(int f, int64 x)
|
||||
{
|
||||
extern int remote_version;
|
||||
@@ -503,10 +607,10 @@ void write_byte(int f,unsigned char c)
|
||||
write_buf(f,(char *)&c,1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int read_line(int f, char *buf, size_t maxlen)
|
||||
{
|
||||
eof_error = 0;
|
||||
|
||||
while (maxlen) {
|
||||
buf[0] = 0;
|
||||
read_buf(f, buf, 1);
|
||||
@@ -525,8 +629,6 @@ int read_line(int f, char *buf, size_t maxlen)
|
||||
return 0;
|
||||
}
|
||||
|
||||
eof_error = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -538,7 +640,7 @@ void io_printf(int fd, const char *format, ...)
|
||||
int len;
|
||||
|
||||
va_start(ap, format);
|
||||
len = vslprintf(buf, sizeof(buf), format, ap);
|
||||
len = vsnprintf(buf, sizeof(buf), format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (len < 0) exit_cleanup(RERR_STREAMIO);
|
||||
@@ -561,31 +663,23 @@ void io_start_multiplex_in(int fd)
|
||||
{
|
||||
multiplex_in_fd = fd;
|
||||
io_flush();
|
||||
if (read_buffer_len) {
|
||||
fprintf(stderr,"ERROR: data in read buffer at mplx start\n");
|
||||
exit_cleanup(RERR_STREAMIO);
|
||||
}
|
||||
|
||||
io_multiplexing_in = 1;
|
||||
}
|
||||
|
||||
/* write an message to the error stream */
|
||||
int io_multiplex_write(int f, char *buf, size_t len)
|
||||
/* write an message to the multiplexed error stream */
|
||||
int io_multiplex_write(enum logcode code, char *buf, size_t len)
|
||||
{
|
||||
if (!io_multiplexing_out) return 0;
|
||||
|
||||
io_flush();
|
||||
|
||||
SIVAL(io_buffer-4, 0, ((MPLEX_BASE + f)<<24) + len);
|
||||
memcpy(io_buffer, buf, len);
|
||||
|
||||
stats.total_written += (len+4);
|
||||
|
||||
writefd_unbuffered(multiplex_out_fd, io_buffer-4, len+4);
|
||||
mplex_write(multiplex_out_fd, code, buf, len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void io_close_input(int fd)
|
||||
/* stop output multiplexing */
|
||||
void io_multiplexing_close(void)
|
||||
{
|
||||
buffer_f_in = -1;
|
||||
io_multiplexing_out = 0;
|
||||
}
|
||||
|
||||
|
||||
357
log.c
357
log.c
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
Copyright (C) Andrew Tridgell 1998
|
||||
/* -*- c-file-style: "linux"; -*-
|
||||
|
||||
Copyright (C) 1998-2001 by Andrew Tridgell <tridge@samba.org>
|
||||
Copyright (C) 2000-2001 by Martin Pool <mbp@samba.org>
|
||||
|
||||
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
|
||||
@@ -17,18 +19,121 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
logging and utility functions
|
||||
|
||||
Logging and utility functions.
|
||||
tridge, May 1998
|
||||
|
||||
Mapping to human-readable messages added by Martin Pool
|
||||
<mbp@samba.org>, Oct 2000.
|
||||
*/
|
||||
#include "rsync.h"
|
||||
|
||||
static char *logfname;
|
||||
static FILE *logfile;
|
||||
static int log_error_fd = -1;
|
||||
|
||||
int log_got_error=0;
|
||||
|
||||
struct {
|
||||
int code;
|
||||
char const *name;
|
||||
} const rerr_names[] = {
|
||||
{ RERR_SYNTAX , "syntax or usage error" },
|
||||
{ RERR_PROTOCOL , "protocol incompatibility" },
|
||||
{ RERR_FILESELECT , "errors selecting input/output files, dirs" },
|
||||
{ RERR_UNSUPPORTED, "requested action not supported" },
|
||||
{ RERR_SOCKETIO , "error in socket IO" },
|
||||
{ RERR_FILEIO , "error in file IO" },
|
||||
{ RERR_STREAMIO , "error in rsync protocol data stream" },
|
||||
{ RERR_MESSAGEIO , "errors with program diagnostics" },
|
||||
{ RERR_IPC , "error in IPC code" },
|
||||
{ RERR_SIGNAL , "received SIGUSR1 or SIGINT" },
|
||||
{ RERR_WAITCHILD , "some error returned by waitpid()" },
|
||||
{ RERR_MALLOC , "error allocating core memory buffers" },
|
||||
{ RERR_PARTIAL , "partial transfer" },
|
||||
{ RERR_TIMEOUT , "timeout in data send/receive" },
|
||||
{ RERR_CMD_FAILED , "remote shell failed" },
|
||||
{ RERR_CMD_KILLED , "remote shell killed" },
|
||||
{ RERR_CMD_RUN, "remote command could not be run" },
|
||||
{ RERR_CMD_NOTFOUND, "remote command not found" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Map from rsync error code to name, or return NULL.
|
||||
*/
|
||||
static char const *rerr_name(int code)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; rerr_names[i].name; i++) {
|
||||
if (rerr_names[i].code == code)
|
||||
return rerr_names[i].name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct err_list {
|
||||
struct err_list *next;
|
||||
char *buf;
|
||||
int len;
|
||||
int written; /* how many bytes we have written so far */
|
||||
};
|
||||
|
||||
static struct err_list *err_list_head;
|
||||
static struct err_list *err_list_tail;
|
||||
|
||||
/* add an error message to the pending error list */
|
||||
static void err_list_add(int code, char *buf, int len)
|
||||
{
|
||||
struct err_list *el;
|
||||
el = (struct err_list *)malloc(sizeof(*el));
|
||||
if (!el) exit_cleanup(RERR_MALLOC);
|
||||
el->next = NULL;
|
||||
el->buf = malloc(len+4);
|
||||
if (!el->buf) exit_cleanup(RERR_MALLOC);
|
||||
memcpy(el->buf+4, buf, len);
|
||||
SIVAL(el->buf, 0, ((code+MPLEX_BASE)<<24) | len);
|
||||
el->len = len+4;
|
||||
el->written = 0;
|
||||
if (err_list_tail) {
|
||||
err_list_tail->next = el;
|
||||
} else {
|
||||
err_list_head = el;
|
||||
}
|
||||
err_list_tail = el;
|
||||
}
|
||||
|
||||
|
||||
/* try to push errors off the error list onto the wire */
|
||||
void err_list_push(void)
|
||||
{
|
||||
if (log_error_fd == -1) return;
|
||||
|
||||
while (err_list_head) {
|
||||
struct err_list *el = err_list_head;
|
||||
int n = write(log_error_fd, el->buf+el->written, el->len - el->written);
|
||||
/* don't check for an error if the best way of handling the error is
|
||||
to ignore it */
|
||||
if (n == -1) break;
|
||||
if (n > 0) {
|
||||
el->written += n;
|
||||
}
|
||||
if (el->written == el->len) {
|
||||
free(el->buf);
|
||||
err_list_head = el->next;
|
||||
if (!err_list_head) err_list_tail = NULL;
|
||||
free(el);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void logit(int priority, char *buf)
|
||||
{
|
||||
if (logfile) {
|
||||
if (logfname) {
|
||||
if (!logfile)
|
||||
log_open();
|
||||
fprintf(logfile,"%s [%d] %s",
|
||||
timestring(time(NULL)), (int)getpid(), buf);
|
||||
fflush(logfile);
|
||||
@@ -37,12 +142,11 @@ static void logit(int priority, char *buf)
|
||||
}
|
||||
}
|
||||
|
||||
void log_open(void)
|
||||
void log_init(void)
|
||||
{
|
||||
static int initialised;
|
||||
int options = LOG_PID;
|
||||
time_t t;
|
||||
char *logf;
|
||||
|
||||
if (initialised) return;
|
||||
initialised = 1;
|
||||
@@ -54,13 +158,13 @@ void log_open(void)
|
||||
localtime(&t);
|
||||
|
||||
/* optionally use a log file instead of syslog */
|
||||
logf = lp_log_file();
|
||||
if (logf && *logf) {
|
||||
extern int orig_umask;
|
||||
int old_umask = umask(022 | orig_umask);
|
||||
logfile = fopen(logf, "a");
|
||||
umask(old_umask);
|
||||
return;
|
||||
logfname = lp_log_file();
|
||||
if (logfname) {
|
||||
if (*logfname) {
|
||||
log_open();
|
||||
return;
|
||||
}
|
||||
logfname = NULL;
|
||||
}
|
||||
|
||||
#ifdef LOG_NDELAY
|
||||
@@ -77,60 +181,88 @@ void log_open(void)
|
||||
logit(LOG_INFO,"rsyncd started\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* this is the rsync debugging function. Call it with FINFO, FERROR or FLOG */
|
||||
void rprintf(int fd, const char *format, ...)
|
||||
void log_open()
|
||||
{
|
||||
if (logfname && !logfile) {
|
||||
extern int orig_umask;
|
||||
int old_umask = umask(022 | orig_umask);
|
||||
logfile = fopen(logfname, "a");
|
||||
umask(old_umask);
|
||||
}
|
||||
}
|
||||
|
||||
void log_close()
|
||||
{
|
||||
if (logfile) {
|
||||
fclose(logfile);
|
||||
logfile = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* setup the error file descriptor - used when we are a server
|
||||
that is receiving files */
|
||||
void set_error_fd(int fd)
|
||||
{
|
||||
log_error_fd = fd;
|
||||
set_nonblocking(log_error_fd);
|
||||
}
|
||||
|
||||
/* this is the underlying (unformatted) rsync debugging function. Call
|
||||
it with FINFO, FERROR or FLOG */
|
||||
void rwrite(enum logcode code, char *buf, int len)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[1024];
|
||||
int len;
|
||||
FILE *f=NULL;
|
||||
extern int am_daemon;
|
||||
extern int am_server;
|
||||
extern int quiet;
|
||||
/* recursion can happen with certain fatal conditions */
|
||||
|
||||
if (quiet != 0 && fd == FINFO) return;
|
||||
|
||||
va_start(ap, format);
|
||||
len = vslprintf(buf, sizeof(buf), format, ap);
|
||||
va_end(ap);
|
||||
if (quiet && code == FINFO) return;
|
||||
|
||||
if (len < 0) exit_cleanup(RERR_MESSAGEIO);
|
||||
|
||||
if (len > sizeof(buf)-1) exit_cleanup(RERR_MESSAGEIO);
|
||||
|
||||
buf[len] = 0;
|
||||
|
||||
if (fd == FLOG) {
|
||||
if (code == FLOG) {
|
||||
if (am_daemon) logit(LOG_INFO, buf);
|
||||
return;
|
||||
}
|
||||
|
||||
/* first try to pass it off to our sibling */
|
||||
if (am_server && log_error_fd != -1) {
|
||||
err_list_add(code, buf, len);
|
||||
err_list_push();
|
||||
return;
|
||||
}
|
||||
|
||||
/* if that fails, try to pass it to the other end */
|
||||
if (am_server && io_multiplex_write(code, buf, len)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (am_daemon) {
|
||||
static int depth;
|
||||
int priority = LOG_INFO;
|
||||
if (fd == FERROR) priority = LOG_WARNING;
|
||||
if (code == FERROR) priority = LOG_WARNING;
|
||||
|
||||
if (depth) return;
|
||||
|
||||
depth++;
|
||||
|
||||
log_open();
|
||||
if (!io_multiplex_write(fd, buf, strlen(buf))) {
|
||||
logit(priority, buf);
|
||||
}
|
||||
log_init();
|
||||
logit(priority, buf);
|
||||
|
||||
depth--;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fd == FERROR) {
|
||||
if (code == FERROR) {
|
||||
log_got_error = 1;
|
||||
f = stderr;
|
||||
}
|
||||
|
||||
if (fd == FINFO) {
|
||||
extern int am_server;
|
||||
if (code == FINFO) {
|
||||
if (am_server)
|
||||
f = stderr;
|
||||
else
|
||||
@@ -143,8 +275,93 @@ void log_open(void)
|
||||
|
||||
if (buf[len-1] == '\r' || buf[len-1] == '\n') fflush(f);
|
||||
}
|
||||
|
||||
|
||||
void rflush(int fd)
|
||||
/* This is the rsync debugging function. Call it with FINFO, FERROR or
|
||||
* FLOG. */
|
||||
void rprintf(enum logcode code, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[1024];
|
||||
int len;
|
||||
|
||||
va_start(ap, format);
|
||||
/* Note: might return -1 */
|
||||
len = vsnprintf(buf, sizeof(buf), format, ap);
|
||||
va_end(ap);
|
||||
|
||||
/* Deal with buffer overruns. Instead of panicking, just
|
||||
* truncate the resulting string. Note that some vsnprintf()s
|
||||
* return -1 on truncation, e.g., glibc 2.0.6 and earlier. */
|
||||
if ((size_t) len > sizeof(buf)-1 || len < 0) {
|
||||
const char ellipsis[] = "[...]";
|
||||
|
||||
/* Reset length, and zero-terminate the end of our buffer */
|
||||
len = sizeof(buf)-1;
|
||||
buf[len] = '\0';
|
||||
|
||||
/* Copy the ellipsis to the end of the string, but give
|
||||
* us one extra character:
|
||||
*
|
||||
* v--- null byte at buf[sizeof(buf)-1]
|
||||
* abcdefghij0
|
||||
* -> abcd[...]00 <-- now two null bytes at end
|
||||
*
|
||||
* If the input format string has a trailing newline,
|
||||
* we copy it into that extra null; if it doesn't, well,
|
||||
* all we lose is one byte. */
|
||||
strncpy(buf+len-sizeof(ellipsis), ellipsis, sizeof(ellipsis));
|
||||
if (format[strlen(format)-1] == '\n') {
|
||||
buf[len-1] = '\n';
|
||||
}
|
||||
}
|
||||
|
||||
rwrite(code, buf, len);
|
||||
}
|
||||
|
||||
|
||||
/* This is like rprintf, but it also tries to print some
|
||||
* representation of the error code. Normally errcode = errno.
|
||||
*
|
||||
* Unlike rprintf, this always adds a newline and there should not be
|
||||
* one in the format string.
|
||||
*
|
||||
* Note that since strerror might involve dynamically loading a
|
||||
* message catalog we need to call it once before chroot-ing. */
|
||||
void rsyserr(enum logcode code, int errcode, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char buf[1024];
|
||||
int len;
|
||||
size_t sys_len;
|
||||
char *sysmsg;
|
||||
|
||||
va_start(ap, format);
|
||||
/* Note: might return <0 */
|
||||
len = vsnprintf(buf, sizeof(buf), format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if ((size_t) len > sizeof(buf)-1)
|
||||
exit_cleanup(RERR_MESSAGEIO);
|
||||
|
||||
sysmsg = strerror(errcode);
|
||||
sys_len = strlen(sysmsg);
|
||||
if ((size_t) len + 3 + sys_len > sizeof(buf) - 1)
|
||||
exit_cleanup(RERR_MESSAGEIO);
|
||||
|
||||
strcpy(buf + len, ": ");
|
||||
len += 2;
|
||||
strcpy(buf + len, sysmsg);
|
||||
len += sys_len;
|
||||
strcpy(buf + len, "\n");
|
||||
len++;
|
||||
|
||||
rwrite(code, buf, len);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void rflush(enum logcode code)
|
||||
{
|
||||
FILE *f = NULL;
|
||||
extern int am_daemon;
|
||||
@@ -153,15 +370,15 @@ void rflush(int fd)
|
||||
return;
|
||||
}
|
||||
|
||||
if (fd == FLOG) {
|
||||
if (code == FLOG) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fd == FERROR) {
|
||||
if (code == FERROR) {
|
||||
f = stderr;
|
||||
}
|
||||
|
||||
if (fd == FINFO) {
|
||||
if (code == FINFO) {
|
||||
extern int am_server;
|
||||
if (am_server)
|
||||
f = stderr;
|
||||
@@ -177,7 +394,7 @@ void rflush(int fd)
|
||||
|
||||
/* a generic logging routine for send/recv, with parameter
|
||||
substitiution */
|
||||
static void log_formatted(int fd,
|
||||
static void log_formatted(enum logcode code,
|
||||
char *format, char *op, struct file_struct *file,
|
||||
struct stats *initial_stats)
|
||||
{
|
||||
@@ -186,14 +403,18 @@ static void log_formatted(int fd,
|
||||
char buf[1024];
|
||||
char buf2[1024];
|
||||
char *p, *s, *n;
|
||||
int l;
|
||||
size_t l;
|
||||
extern struct stats stats;
|
||||
extern int am_sender;
|
||||
extern int am_daemon;
|
||||
int64 b;
|
||||
|
||||
memset(buf,0,sizeof(buf));
|
||||
|
||||
/* We expand % codes one by one in place in buf. We don't
|
||||
* copy in the terminating nul of the inserted strings, but
|
||||
* rather keep going until we reach the nul of the format.
|
||||
* Just to make sure we don't clobber that nul and therefore
|
||||
* accidentally keep going, we zero the buffer now. */
|
||||
memset(buf, 0, sizeof buf);
|
||||
strlcpy(buf, format, sizeof(buf));
|
||||
|
||||
for (s=&buf[0];
|
||||
@@ -205,18 +426,18 @@ static void log_formatted(int fd,
|
||||
case 'h': if (am_daemon) n = client_name(0); break;
|
||||
case 'a': if (am_daemon) n = client_addr(0); break;
|
||||
case 'l':
|
||||
slprintf(buf2,sizeof(buf2),"%.0f",
|
||||
snprintf(buf2,sizeof(buf2),"%.0f",
|
||||
(double)file->length);
|
||||
n = buf2;
|
||||
break;
|
||||
case 'p':
|
||||
slprintf(buf2,sizeof(buf2),"%d",
|
||||
snprintf(buf2,sizeof(buf2),"%d",
|
||||
(int)getpid());
|
||||
n = buf2;
|
||||
break;
|
||||
case 'o': n = op; break;
|
||||
case 'f':
|
||||
slprintf(buf2, sizeof(buf2), "%s/%s",
|
||||
snprintf(buf2, sizeof(buf2), "%s/%s",
|
||||
file->basedir?file->basedir:"",
|
||||
f_name(file));
|
||||
clean_fname(buf2);
|
||||
@@ -235,7 +456,7 @@ static void log_formatted(int fd,
|
||||
b = stats.total_read -
|
||||
initial_stats->total_read;
|
||||
}
|
||||
slprintf(buf2,sizeof(buf2),"%.0f", (double)b);
|
||||
snprintf(buf2,sizeof(buf2),"%.0f", (double)b);
|
||||
n = buf2;
|
||||
break;
|
||||
case 'c':
|
||||
@@ -246,30 +467,39 @@ static void log_formatted(int fd,
|
||||
b = stats.total_read -
|
||||
initial_stats->total_read;
|
||||
}
|
||||
slprintf(buf2,sizeof(buf2),"%.0f", (double)b);
|
||||
snprintf(buf2,sizeof(buf2),"%.0f", (double)b);
|
||||
n = buf2;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!n) continue;
|
||||
/* n is the string to be inserted in place of this %
|
||||
* code; l is its length not including the trailing
|
||||
* NUL */
|
||||
if (!n)
|
||||
continue;
|
||||
|
||||
l = strlen(n);
|
||||
|
||||
if (l + ((int)(s - &buf[0])) > sizeof(buf)) {
|
||||
if (l + ((int)(s - &buf[0])) >= sizeof(buf)) {
|
||||
rprintf(FERROR,"buffer overflow expanding %%%c - exiting\n",
|
||||
p[0]);
|
||||
exit_cleanup(RERR_MESSAGEIO);
|
||||
}
|
||||
|
||||
/* Shuffle the rest of the string along to make space for n */
|
||||
if (l != 2) {
|
||||
memmove(s+(l-1), s+1, strlen(s+1)+1);
|
||||
}
|
||||
|
||||
/* Copy in n but NOT its nul, because the format sting
|
||||
* probably continues after this. */
|
||||
memcpy(p, n, l);
|
||||
|
||||
/* Skip over inserted string; continue looking */
|
||||
s = p+l;
|
||||
}
|
||||
|
||||
rprintf(fd,"%s\n", buf);
|
||||
rprintf(code,"%s\n", buf);
|
||||
}
|
||||
|
||||
/* log the outgoing transfer of a file */
|
||||
@@ -300,7 +530,15 @@ void log_recv(struct file_struct *file, struct stats *initial_stats)
|
||||
}
|
||||
}
|
||||
|
||||
/* called when the transfer is interrupted for some reason */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Called when the transfer is interrupted for some reason.
|
||||
*
|
||||
* Code is one of the RERR_* codes from errcode.h, or terminating
|
||||
* successfully.
|
||||
*/
|
||||
void log_exit(int code, const char *file, int line)
|
||||
{
|
||||
if (code == 0) {
|
||||
@@ -310,11 +548,20 @@ void log_exit(int code, const char *file, int line)
|
||||
(double)stats.total_read,
|
||||
(double)stats.total_size);
|
||||
} else {
|
||||
rprintf(FLOG,"transfer interrupted (code %d) at %s(%d)\n",
|
||||
code, file, line);
|
||||
const char *name;
|
||||
|
||||
name = rerr_name(code);
|
||||
if (!name)
|
||||
name = "unexplained error";
|
||||
|
||||
rprintf(FERROR,"rsync error: %s (code %d) at %s(%d)\n",
|
||||
name, code, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* log the incoming transfer of a file for interactive use, this
|
||||
will be called at the end where the client was run
|
||||
|
||||
|
||||
1
main.c
1
main.c
@@ -60,6 +60,7 @@ static void report(int f)
|
||||
if (do_stats) {
|
||||
/* These come out from every process */
|
||||
show_malloc_stats();
|
||||
show_flist_stats();
|
||||
}
|
||||
|
||||
if (am_daemon) {
|
||||
|
||||
6
match.c
6
match.c
@@ -71,7 +71,7 @@ static void build_hash_table(struct sum_struct *s)
|
||||
if (!tag_table || !targets)
|
||||
out_of_memory("build_hash_table");
|
||||
|
||||
for (i=0;i<s->count;i++) {
|
||||
for (i=0;i<(int) s->count;i++) {
|
||||
targets[i].i = i;
|
||||
targets[i].t = gettag(s->sums[i].sum1);
|
||||
}
|
||||
@@ -175,7 +175,7 @@ static void hash_search(int f,struct sum_struct *s,
|
||||
|
||||
sum = (s1 & 0xffff) | (s2 << 16);
|
||||
tag_hits++;
|
||||
for (; j<s->count && targets[j].t == t; j++) {
|
||||
for (; j < (int) s->count && targets[j].t == t; j++) {
|
||||
int l, i = targets[j].i;
|
||||
|
||||
if (sum != s->sums[i].sum1) continue;
|
||||
@@ -201,7 +201,7 @@ static void hash_search(int f,struct sum_struct *s,
|
||||
|
||||
/* we've found a match, but now check to see
|
||||
if last_i can hint at a better match */
|
||||
for (j++; j<s->count && targets[j].t == t; j++) {
|
||||
for (j++; j < (int) s->count && targets[j].t == t; j++) {
|
||||
int i2 = targets[j].i;
|
||||
if (i2 == last_i + 1) {
|
||||
if (sum != s->sums[i2].sum1) break;
|
||||
|
||||
@@ -65,6 +65,7 @@ int size_only=0;
|
||||
int bwlimit=0;
|
||||
int delete_after=0;
|
||||
int only_existing=0;
|
||||
int opt_ignore_existing=0;
|
||||
int max_delete=0;
|
||||
int ignore_errors=0;
|
||||
#ifdef _WIN32
|
||||
@@ -208,6 +209,7 @@ void usage(enum logcode F)
|
||||
rprintf(F," --rsync-path=PATH specify path to rsync on the remote machine\n");
|
||||
rprintf(F," -C, --cvs-exclude auto ignore files in the same way CVS does\n");
|
||||
rprintf(F," --existing only update files that already exist\n");
|
||||
rprintf(F," --ignore-existing ignore files that already exist on the receiving side\n");
|
||||
rprintf(F," --delete delete files that don't exist on the sending side\n");
|
||||
rprintf(F," --delete-excluded also delete excluded files on the receiving side\n");
|
||||
rprintf(F," --delete-after delete after transferring, not before\n");
|
||||
@@ -262,7 +264,7 @@ enum {OPT_VERSION = 1000, OPT_SUFFIX, OPT_SENDER, OPT_SERVER, OPT_EXCLUDE,
|
||||
OPT_LOG_FORMAT, OPT_PASSWORD_FILE, OPT_SIZE_ONLY, OPT_ADDRESS,
|
||||
OPT_DELETE_AFTER, OPT_EXISTING, OPT_MAX_DELETE, OPT_BACKUP_DIR,
|
||||
OPT_IGNORE_ERRORS, OPT_BWLIMIT, OPT_BLOCKING_IO,
|
||||
OPT_MODIFY_WINDOW, OPT_READ_BATCH, OPT_WRITE_BATCH};
|
||||
OPT_MODIFY_WINDOW, OPT_READ_BATCH, OPT_WRITE_BATCH, OPT_IGNORE_EXISTING};
|
||||
|
||||
static struct poptOption long_options[] = {
|
||||
/* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
|
||||
@@ -276,6 +278,7 @@ static struct poptOption long_options[] = {
|
||||
{"one-file-system", 'x', POPT_ARG_NONE, &one_file_system},
|
||||
{"delete", 0, POPT_ARG_NONE, &delete_mode},
|
||||
{"existing", 0, POPT_ARG_NONE, &only_existing},
|
||||
{"ignore-existing", 0, POPT_ARG_NONE, &opt_ignore_existing},
|
||||
{"delete-after", 0, POPT_ARG_NONE, &delete_after},
|
||||
{"delete-excluded", 0, POPT_ARG_NONE, 0, OPT_DELETE_EXCLUDED},
|
||||
{"force", 0, POPT_ARG_NONE, &force_delete},
|
||||
@@ -691,6 +694,9 @@ void server_options(char **args,int *argc)
|
||||
if (only_existing && am_sender)
|
||||
args[ac++] = "--existing";
|
||||
|
||||
if (opt_ignore_existing && am_sender)
|
||||
args[ac++] = "--ignore-existing";
|
||||
|
||||
if (tmpdir) {
|
||||
args[ac++] = "--temp-dir";
|
||||
args[ac++] = tmpdir;
|
||||
|
||||
98
receiver.c
98
receiver.c
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright (C) Andrew Tridgell 1996
|
||||
/* -*- c-file-style: "linux" -*-
|
||||
|
||||
Copyright (C) 1996-2000 by Andrew Tridgell
|
||||
Copyright (C) Paul Mackerras 1996
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
@@ -36,14 +37,12 @@ extern char *compare_dest;
|
||||
extern int make_backups;
|
||||
extern char *backup_suffix;
|
||||
|
||||
|
||||
static struct delete_list {
|
||||
dev_t dev;
|
||||
INO_T inode;
|
||||
DEV64_T dev;
|
||||
INO64_T inode;
|
||||
} *delete_list;
|
||||
static int dlist_len, dlist_alloc_len;
|
||||
|
||||
|
||||
/* yuck! This function wouldn't have been necessary if I had the sorting
|
||||
algorithm right. Unfortunately fixing the sorting algorithm would introduce
|
||||
a backward incompatibility as file list indexes are sent over the link.
|
||||
@@ -84,14 +83,15 @@ static void delete_one(struct file_struct *f)
|
||||
{
|
||||
if (!S_ISDIR(f->mode)) {
|
||||
if (robust_unlink(f_name(f)) != 0) {
|
||||
rprintf(FERROR,"unlink %s : %s\n",f_name(f),strerror(errno));
|
||||
rprintf(FERROR,"delete_one: unlink %s: %s\n",f_name(f),strerror(errno));
|
||||
} else if (verbose) {
|
||||
rprintf(FINFO,"deleting %s\n",f_name(f));
|
||||
}
|
||||
} else {
|
||||
if (do_rmdir(f_name(f)) != 0) {
|
||||
if (errno != ENOTEMPTY && errno != EEXIST)
|
||||
rprintf(FERROR,"rmdir %s : %s\n",f_name(f),strerror(errno));
|
||||
rprintf(FERROR,"delete_one: rmdir %s: %s\n",
|
||||
f_name(f), strerror(errno));
|
||||
} else if (verbose) {
|
||||
rprintf(FINFO,"deleting directory %s\n",f_name(f));
|
||||
}
|
||||
@@ -104,17 +104,20 @@ static void delete_one(struct file_struct *f)
|
||||
/* this deletes any files on the receiving side that are not present
|
||||
on the sending side. For version 1.6.4 I have changed the behaviour
|
||||
to match more closely what most people seem to expect of this option */
|
||||
static void delete_files(struct file_list *flist)
|
||||
void delete_files(struct file_list *flist)
|
||||
{
|
||||
struct file_list *local_file_list;
|
||||
int i, j;
|
||||
char *name;
|
||||
extern int module_id;
|
||||
extern int ignore_errors;
|
||||
extern int max_delete;
|
||||
static int deletion_count;
|
||||
|
||||
if (cvs_exclude)
|
||||
add_cvs_excludes();
|
||||
|
||||
if (io_error && !lp_ignore_errors(module_id)) {
|
||||
if (io_error && !(lp_ignore_errors(module_id) || ignore_errors)) {
|
||||
rprintf(FINFO,"IO error encountered - skipping file deletion\n");
|
||||
return;
|
||||
}
|
||||
@@ -137,6 +140,7 @@ static void delete_files(struct file_list *flist)
|
||||
rprintf(FINFO,"deleting in %s\n", name);
|
||||
|
||||
for (i=local_file_list->count-1;i>=0;i--) {
|
||||
if (max_delete && deletion_count > max_delete) break;
|
||||
if (!local_file_list->files[i]->basename) continue;
|
||||
if (remote_version < 19 &&
|
||||
S_ISDIR(local_file_list->files[i]->mode))
|
||||
@@ -144,10 +148,12 @@ static void delete_files(struct file_list *flist)
|
||||
if (-1 == flist_find(flist,local_file_list->files[i])) {
|
||||
char *f = f_name(local_file_list->files[i]);
|
||||
int k = strlen(f) - strlen(backup_suffix);
|
||||
/* Hi Andrew, do we really need to play with backup_suffix here? */
|
||||
if (make_backups && ((k <= 0) ||
|
||||
(strcmp(f+k,backup_suffix) != 0))) {
|
||||
(void) make_backup(f);
|
||||
} else {
|
||||
deletion_count++;
|
||||
delete_one(local_file_list->files[i]);
|
||||
}
|
||||
}
|
||||
@@ -173,7 +179,7 @@ static int get_tmpname(char *fnametmp, char *fname)
|
||||
rprintf(FERROR,"filename too long\n");
|
||||
return 0;
|
||||
}
|
||||
slprintf(fnametmp,MAXPATHLEN, "%s/.%s.XXXXXX",tmpdir,f);
|
||||
snprintf(fnametmp,MAXPATHLEN, "%s/.%s.XXXXXX",tmpdir,f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -186,11 +192,11 @@ static int get_tmpname(char *fnametmp, char *fname)
|
||||
|
||||
if (f) {
|
||||
*f = 0;
|
||||
slprintf(fnametmp,MAXPATHLEN,"%s/.%s.XXXXXX",
|
||||
snprintf(fnametmp,MAXPATHLEN,"%s/.%s.XXXXXX",
|
||||
fname,f+1);
|
||||
*f = '/';
|
||||
} else {
|
||||
slprintf(fnametmp,MAXPATHLEN,".%s.XXXXXX",fname);
|
||||
snprintf(fnametmp,MAXPATHLEN,".%s.XXXXXX",fname);
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -223,8 +229,8 @@ static int receive_data(int f_in,struct map_struct *buf,int fd,char *fname,
|
||||
extern int cleanup_got_literal;
|
||||
|
||||
if (verbose > 3) {
|
||||
rprintf(FINFO,"data recv %d at %d\n",
|
||||
i,(int)offset);
|
||||
rprintf(FINFO,"data recv %d at %.0f\n",
|
||||
i,(double)offset);
|
||||
}
|
||||
|
||||
stats.literal_data += i;
|
||||
@@ -241,23 +247,25 @@ static int receive_data(int f_in,struct map_struct *buf,int fd,char *fname,
|
||||
}
|
||||
|
||||
i = -(i+1);
|
||||
offset2 = i*n;
|
||||
offset2 = i*(OFF_T)n;
|
||||
len = n;
|
||||
if (i == count-1 && remainder != 0)
|
||||
if (i == (int) count-1 && remainder != 0)
|
||||
len = remainder;
|
||||
|
||||
stats.matched_data += len;
|
||||
|
||||
if (verbose > 3)
|
||||
rprintf(FINFO,"chunk[%d] of size %d at %d offset=%d\n",
|
||||
i,len,(int)offset2,(int)offset);
|
||||
rprintf(FINFO,"chunk[%d] of size %d at %.0f offset=%.0f\n",
|
||||
i,len,(double)offset2,(double)offset);
|
||||
|
||||
map = map_ptr(buf,offset2,len);
|
||||
if (buf) {
|
||||
map = map_ptr(buf,offset2,len);
|
||||
|
||||
see_token(map, len);
|
||||
sum_update(map,len);
|
||||
see_token(map, len);
|
||||
sum_update(map,len);
|
||||
}
|
||||
|
||||
if (fd != -1 && write_file(fd,map,len) != len) {
|
||||
if (fd != -1 && write_file(fd,map,len) != (int) len) {
|
||||
rprintf(FERROR,"write failed on %s : %s\n",
|
||||
fname,strerror(errno));
|
||||
exit_cleanup(RERR_FILEIO);
|
||||
@@ -265,7 +273,7 @@ static int receive_data(int f_in,struct map_struct *buf,int fd,char *fname,
|
||||
offset += len;
|
||||
}
|
||||
|
||||
end_progress();
|
||||
end_progress(total_size);
|
||||
|
||||
if (fd != -1 && offset > 0 && sparse_end(fd) != 0) {
|
||||
rprintf(FERROR,"write failed on %s : %s\n",
|
||||
@@ -289,12 +297,15 @@ static int receive_data(int f_in,struct map_struct *buf,int fd,char *fname,
|
||||
}
|
||||
|
||||
|
||||
/* main routine for receiver process. Receiver process runs on the
|
||||
same host as the generator process. */
|
||||
|
||||
int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen)
|
||||
{
|
||||
int fd1,fd2;
|
||||
STRUCT_STAT st;
|
||||
char *fname;
|
||||
char template[MAXPATHLEN];
|
||||
char fnametmp[MAXPATHLEN];
|
||||
char *fnamecmp;
|
||||
char fnamecmpbuf[MAXPATHLEN];
|
||||
@@ -312,12 +323,6 @@ int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen)
|
||||
rprintf(FINFO,"recv_files(%d) starting\n",flist->count);
|
||||
}
|
||||
|
||||
if (!delete_after) {
|
||||
if (recurse && delete_mode && !local_name && flist->count>0) {
|
||||
delete_files(flist);
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
cleanup_disable();
|
||||
|
||||
@@ -368,7 +373,7 @@ int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen)
|
||||
|
||||
if ((fd1 == -1) && (compare_dest != NULL)) {
|
||||
/* try the file at compare_dest instead */
|
||||
slprintf(fnamecmpbuf,MAXPATHLEN,"%s/%s",
|
||||
snprintf(fnamecmpbuf,MAXPATHLEN,"%s/%s",
|
||||
compare_dest,fname);
|
||||
fnamecmp = fnamecmpbuf;
|
||||
fd1 = do_open(fnamecmp, O_RDONLY, 0);
|
||||
@@ -398,7 +403,7 @@ int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen)
|
||||
if (fd1 != -1 && st.st_size > 0) {
|
||||
buf = map_file(fd1,st.st_size);
|
||||
if (verbose > 2)
|
||||
rprintf(FINFO,"recv mapped %s of size %d\n",fnamecmp,(int)st.st_size);
|
||||
rprintf(FINFO,"recv mapped %s of size %.0f\n",fnamecmp,(double)st.st_size);
|
||||
} else {
|
||||
buf = NULL;
|
||||
}
|
||||
@@ -409,17 +414,7 @@ int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* mktemp is deliberately used here instead of mkstemp.
|
||||
because O_EXCL is used on the open, the race condition
|
||||
is not a problem or a security hole, and we want to
|
||||
control the access permissions on the created file. */
|
||||
if (NULL == do_mktemp(fnametmp)) {
|
||||
rprintf(FERROR,"mktemp %s failed\n",fnametmp);
|
||||
receive_data(f_in,buf,-1,NULL,file->length);
|
||||
if (buf) unmap_file(buf);
|
||||
if (fd1 != -1) close(fd1);
|
||||
continue;
|
||||
}
|
||||
strlcpy(template, fnametmp, sizeof(template));
|
||||
|
||||
/* we initially set the perms without the
|
||||
setuid/setgid bits to ensure that there is no race
|
||||
@@ -427,16 +422,21 @@ int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen)
|
||||
the lchown. Thanks to snabb@epipe.fi for pointing
|
||||
this out. We also set it initially without group
|
||||
access because of a similar race condition. */
|
||||
fd2 = do_open(fnametmp,O_WRONLY|O_CREAT|O_EXCL,
|
||||
file->mode & INITACCESSPERMS);
|
||||
fd2 = do_mkstemp(fnametmp, file->mode & INITACCESSPERMS);
|
||||
if (fd2 == -1) {
|
||||
rprintf(FERROR,"mkstemp %s failed\n",fnametmp);
|
||||
receive_data(f_in,buf,-1,NULL,file->length);
|
||||
if (buf) unmap_file(buf);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* in most cases parent directories will already exist
|
||||
because their information should have been previously
|
||||
transferred, but that may not be the case with -R */
|
||||
if (fd2 == -1 && relative_paths && errno == ENOENT &&
|
||||
create_directory_path(fnametmp) == 0) {
|
||||
fd2 = do_open(fnametmp,O_WRONLY|O_CREAT|O_EXCL,
|
||||
file->mode & INITACCESSPERMS);
|
||||
strlcpy(fnametmp, template, sizeof(fnametmp));
|
||||
fd2 = do_mkstemp(fnametmp, file->mode & INITACCESSPERMS);
|
||||
}
|
||||
if (fd2 == -1) {
|
||||
rprintf(FERROR,"cannot create %s : %s\n",fnametmp,strerror(errno));
|
||||
@@ -469,7 +469,7 @@ int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen)
|
||||
finish_transfer(fname, fnametmp, file);
|
||||
|
||||
cleanup_disable();
|
||||
|
||||
|
||||
if (!recv_ok) {
|
||||
if (csum_length == SUM_LENGTH) {
|
||||
rprintf(FERROR,"ERROR: file corruption in %s. File changed during transfer?\n",
|
||||
@@ -489,7 +489,7 @@ int recv_files(int f_in,struct file_list *flist,char *local_name,int f_gen)
|
||||
}
|
||||
|
||||
if (preserve_hard_links)
|
||||
do_hard_links(flist);
|
||||
do_hard_links();
|
||||
|
||||
/* now we need to fix any directory permissions that were
|
||||
modified during the transfer */
|
||||
|
||||
174
rsync.h
174
rsync.h
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
Copyright (C) Andrew Tridgell 1996
|
||||
Copyright (C) by Andrew Tridgell 1996, 2000
|
||||
Copyright (C) Paul Mackerras 1996
|
||||
Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
|
||||
|
||||
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
|
||||
@@ -17,6 +18,7 @@
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
|
||||
#define False 0
|
||||
#define True 1
|
||||
|
||||
@@ -47,8 +49,19 @@
|
||||
#define SAME_TIME (1<<7)
|
||||
|
||||
/* update this if you make incompatible changes */
|
||||
#define PROTOCOL_VERSION 21
|
||||
#define MIN_PROTOCOL_VERSION 11
|
||||
#define PROTOCOL_VERSION 26
|
||||
|
||||
/* We refuse to interoperate with versions that are not in this range.
|
||||
* Note that we assume we'll work with later versions: the onus is on
|
||||
* people writing them to make sure that they don't send us anything
|
||||
* we won't understand.
|
||||
*
|
||||
* There are two possible explanations for the limit at thirty: either
|
||||
* to allow new major-rev versions that do not interoperate with us,
|
||||
* and (more likely) so that we can detect an attempt to connect rsync
|
||||
* to a non-rsync server, which is unlikely to begin by sending a byte
|
||||
* between 15 and 30. */
|
||||
#define MIN_PROTOCOL_VERSION 15
|
||||
#define MAX_PROTOCOL_VERSION 30
|
||||
|
||||
#define RSYNC_PORT 873
|
||||
@@ -58,14 +71,15 @@
|
||||
#define CHUNK_SIZE (32*1024)
|
||||
#define MAX_MAP_SIZE (256*1024)
|
||||
#define IO_BUFFER_SIZE (4092)
|
||||
#define MAX_READ_BUFFER (1024*1024)
|
||||
|
||||
#define MAX_ARGS 1000
|
||||
|
||||
#define MPLEX_BASE 7
|
||||
#define FERROR 1
|
||||
#define FINFO 2
|
||||
#define FLOG 3
|
||||
|
||||
/* Log values. I *think* what these mean is: FLOG goes to the server
|
||||
* logfile; FERROR and FINFO try to end up on the client, with
|
||||
* different levels of filtering. */
|
||||
enum logcode {FNONE=0, FERROR=1, FINFO=2, FLOG=3 };
|
||||
|
||||
#include "errcode.h"
|
||||
|
||||
@@ -79,12 +93,6 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
#include <getopt.h>
|
||||
#else
|
||||
#include "lib/getopt.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
@@ -177,6 +185,10 @@
|
||||
#include <glob.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MALLOC_H
|
||||
# include <malloc.h>
|
||||
#endif
|
||||
|
||||
/* these are needed for the uid/gid mapping code */
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
@@ -255,15 +267,44 @@
|
||||
#elif HAVE_LONGLONG
|
||||
#define int64 long long
|
||||
#else
|
||||
/* As long as it gets... */
|
||||
#define int64 off_t
|
||||
#define NO_INT64
|
||||
#endif
|
||||
|
||||
#if HAVE_SHORT_INO_T
|
||||
#define INO_T uint32
|
||||
#else
|
||||
#define INO_T ino_t
|
||||
#endif
|
||||
/* Starting from protocol version 26, we always use 64-bit
|
||||
* ino_t and dev_t internally, even if this platform does not
|
||||
* allow files to have 64-bit inums. That's because the
|
||||
* receiver needs to find duplicate (dev,ino) tuples to detect
|
||||
* hardlinks, and it might have files coming from a platform
|
||||
* that has 64-bit inums.
|
||||
*
|
||||
* The only exception is if we're on a platform with no 64-bit type at
|
||||
* all.
|
||||
*
|
||||
* Because we use read_longint() to get these off the wire, if you
|
||||
* transfer devices or hardlinks with dev or inum > 2**32 to a machine
|
||||
* with no 64-bit types then you will get an overflow error. Probably
|
||||
* not many people have that combination of machines, and you can
|
||||
* avoid it by not preserving hardlinks or not transferring device
|
||||
* nodes. It's not clear that any other behaviour is better.
|
||||
*
|
||||
* Note that if you transfer devices from a 64-bit-devt machine (say,
|
||||
* Solaris) to a 32-bit-devt machine (say, Linux-2.2/x86) then the
|
||||
* device numbers will be truncated. But it's a kind of silly thing
|
||||
* to do anyhow.
|
||||
*
|
||||
* FIXME: In future, we should probable split the device number into
|
||||
* major/minor, and transfer the two parts as 32-bit ints. That gives
|
||||
* you somewhat more of a chance that they'll come from a big machine
|
||||
* to a little one in a useful way.
|
||||
*
|
||||
* FIXME: Really we need an unsigned type, and we perhaps ought to
|
||||
* cope with platforms on which this is an unsigned int or even a
|
||||
* struct. Later.
|
||||
*/
|
||||
#define INO64_T unsigned int64
|
||||
#define DEV64_T unsigned int64
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a,b) ((a)<(b)?(a):(b))
|
||||
@@ -294,9 +335,13 @@ struct file_struct {
|
||||
time_t modtime;
|
||||
OFF_T length;
|
||||
mode_t mode;
|
||||
INO_T inode;
|
||||
dev_t dev;
|
||||
dev_t rdev;
|
||||
|
||||
INO64_T inode;
|
||||
/** Device this file lives upon */
|
||||
DEV64_T dev;
|
||||
|
||||
/** If this is a device node, the device number. */
|
||||
DEV64_T rdev;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
char *basename;
|
||||
@@ -307,10 +352,20 @@ struct file_struct {
|
||||
};
|
||||
|
||||
|
||||
#define ARENA_SIZE (32 * 1024)
|
||||
|
||||
struct string_area {
|
||||
char *base;
|
||||
char *end;
|
||||
char *current;
|
||||
struct string_area *next;
|
||||
};
|
||||
|
||||
struct file_list {
|
||||
int count;
|
||||
int malloced;
|
||||
struct file_struct **files;
|
||||
struct string_area *string_area;
|
||||
};
|
||||
|
||||
struct sum_buf {
|
||||
@@ -322,11 +377,11 @@ struct sum_buf {
|
||||
};
|
||||
|
||||
struct sum_struct {
|
||||
OFF_T flength; /* total file length */
|
||||
size_t count; /* how many chunks */
|
||||
size_t remainder; /* flength % block_length */
|
||||
size_t n; /* block_length */
|
||||
struct sum_buf *sums; /* points to info for each chunk */
|
||||
OFF_T flength; /* total file length */
|
||||
size_t count; /* how many chunks */
|
||||
size_t remainder; /* flength % block_length */
|
||||
size_t n; /* block_length */
|
||||
struct sum_buf *sums; /* points to info for each chunk */
|
||||
};
|
||||
|
||||
struct map_struct {
|
||||
@@ -336,7 +391,6 @@ struct map_struct {
|
||||
};
|
||||
|
||||
struct exclude_struct {
|
||||
char *orig;
|
||||
char *pattern;
|
||||
int regular_exp;
|
||||
int fnmatch_flags;
|
||||
@@ -368,9 +422,29 @@ static inline int flist_up(struct file_list *flist, int i)
|
||||
}
|
||||
|
||||
#include "byteorder.h"
|
||||
#include "version.h"
|
||||
#include "proto.h"
|
||||
#include "lib/mdfour.h"
|
||||
#include "lib/permstring.h"
|
||||
#include "lib/addrinfo.h"
|
||||
|
||||
#include "proto.h"
|
||||
|
||||
/* We have replacement versions of these if they're missing. */
|
||||
#ifndef HAVE_ASPRINTF
|
||||
int asprintf(char **ptr, const char *format, ...);
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_VASPRINTF
|
||||
int vasprintf(char **ptr, const char *format, va_list ap);
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_VSNPRINTF) && !defined(HAVE_C99_VSNPRINTF)
|
||||
int vsnprintf (char *str, size_t count, const char *fmt, va_list args);
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_SNPRINTF) && !defined(HAVE_C99_VSNPRINTF)
|
||||
int snprintf(char *str,size_t count,const char *fmt,...);
|
||||
#endif
|
||||
|
||||
|
||||
#if !HAVE_STRERROR
|
||||
extern char *sys_errlist[];
|
||||
@@ -459,6 +533,22 @@ extern int errno;
|
||||
#define S_ISREG(mode) (((mode) & (_S_IFMT)) == (_S_IFREG))
|
||||
#endif
|
||||
|
||||
/* work out what fcntl flag to use for non-blocking */
|
||||
#ifdef O_NONBLOCK
|
||||
# define NONBLOCK_FLAG O_NONBLOCK
|
||||
#elif defined(SYSV)
|
||||
# define NONBLOCK_FLAG O_NDELAY
|
||||
#else
|
||||
# define NONBLOCK_FLAG FNDELAY
|
||||
#endif
|
||||
|
||||
#ifndef INADDR_LOOPBACK
|
||||
#define INADDR_LOOPBACK 0x7f000001
|
||||
#endif
|
||||
|
||||
#ifndef INADDR_NONE
|
||||
#define INADDR_NONE 0xffffffff
|
||||
#endif
|
||||
|
||||
#define IS_DEVICE(mode) (S_ISCHR(mode) || S_ISBLK(mode) || S_ISSOCK(mode) || S_ISFIFO(mode))
|
||||
|
||||
@@ -474,12 +564,20 @@ extern int errno;
|
||||
#define NS(s) ((s)?(s):"<NULL>")
|
||||
|
||||
/* use magic gcc attributes to catch format errors */
|
||||
void rprintf(int , const char *, ...)
|
||||
void rprintf(enum logcode , const char *, ...)
|
||||
#ifdef __GNUC__
|
||||
__attribute__ ((format (printf, 2, 3)))
|
||||
#endif
|
||||
;
|
||||
|
||||
/* This is just like rprintf, but it also tries to print some
|
||||
* representation of the error code. Normally errcode = errno. */
|
||||
void rsyserr(enum logcode, int, const char *, ...)
|
||||
#ifdef __GNUC__
|
||||
__attribute__ ((format (printf, 3, 4)))
|
||||
#endif
|
||||
;
|
||||
|
||||
#ifdef REPLACE_INET_NTOA
|
||||
#define inet_ntoa rep_inet_ntoa
|
||||
#endif
|
||||
@@ -493,4 +591,20 @@ size_t strlcpy(char *d, const char *s, size_t bufsize);
|
||||
size_t strlcat(char *d, const char *s, size_t bufsize);
|
||||
#endif
|
||||
|
||||
#ifndef WEXITSTATUS
|
||||
#define WEXITSTATUS(stat) ((int)(((stat)>>8)&0xFF))
|
||||
#endif
|
||||
|
||||
#define exit_cleanup(code) _exit_cleanup(code, __FILE__, __LINE__)
|
||||
|
||||
|
||||
extern int verbose;
|
||||
|
||||
#ifndef HAVE_INET_NTOP
|
||||
const char *
|
||||
inet_ntop(int af, const void *src, char *dst, size_t size);
|
||||
#endif /* !HAVE_INET_NTOP */
|
||||
|
||||
#ifndef HAVE_INET_PTON
|
||||
int isc_net_pton(int af, const char *src, void *dst);
|
||||
#endif
|
||||
|
||||
7
rsync.yo
7
rsync.yo
@@ -1,5 +1,5 @@
|
||||
mailto(rsync-bugs@samba.org)
|
||||
manpage(rsync)(1)(14 Dec 2001)()()
|
||||
manpage(rsync)(1)(25 Jan 2002)()()
|
||||
manpagename(rsync)(faster, flexible replacement for rcp)
|
||||
manpagesynopsis()
|
||||
|
||||
@@ -245,6 +245,7 @@ verb(
|
||||
--rsync-path=PATH specify path to rsync on the remote machine
|
||||
-C, --cvs-exclude auto ignore files in the same way CVS does
|
||||
--existing only update files that already exist
|
||||
--ignore-existing ignore files that already exist on the receiving side
|
||||
--delete delete files that don't exist on the sending side
|
||||
--delete-excluded also delete excluded files on the receiving side
|
||||
--delete-after delete after transferring, not before
|
||||
@@ -452,6 +453,10 @@ contents of only one filesystem.
|
||||
dit(bf(--existing)) This tells rsync not to create any new files -
|
||||
only update files that already exist on the destination.
|
||||
|
||||
dit(bf(--ignore-existing))
|
||||
This tells rsync not to update files that already exist on
|
||||
the destination.
|
||||
|
||||
dit(bf(--max-delete=NUM)) This tells rsync not to delete more than NUM
|
||||
files or directories. This is useful when mirroring very large trees
|
||||
to prevent disasters.
|
||||
|
||||
4
sender.c
4
sender.c
@@ -55,14 +55,14 @@ static struct sum_struct *receive_sums(int f)
|
||||
s->sums = (struct sum_buf *)malloc(sizeof(s->sums[0])*s->count);
|
||||
if (!s->sums) out_of_memory("receive_sums");
|
||||
|
||||
for (i=0;i<s->count;i++) {
|
||||
for (i=0; i < (int) s->count;i++) {
|
||||
s->sums[i].sum1 = read_int(f);
|
||||
read_buf(f,s->sums[i].sum2,csum_length);
|
||||
|
||||
s->sums[i].offset = offset;
|
||||
s->sums[i].i = i;
|
||||
|
||||
if (i == s->count-1 && s->remainder != 0) {
|
||||
if (i == (int) s->count-1 && s->remainder != 0) {
|
||||
s->sums[i].len = s->remainder;
|
||||
} else {
|
||||
s->sums[i].len = s->n;
|
||||
|
||||
29
testsuite/devices.test
Normal file
29
testsuite/devices.test
Normal file
@@ -0,0 +1,29 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Copyright (C) 2002 by Martin Pool <mbp@samba.org>
|
||||
|
||||
# This program is distributable under the terms of the GNU GPL (see
|
||||
# COPYING).
|
||||
|
||||
# Test rsync handling of devices. This can only run if you're root.
|
||||
|
||||
. $srcdir/testsuite/rsync.fns
|
||||
|
||||
set -x
|
||||
|
||||
# Build some hardlinks
|
||||
|
||||
fromdir="$scratchdir/from"
|
||||
todir="$scratchdir/to"
|
||||
|
||||
# TODO: Need to test whether hardlinks are possible on this OS/filesystem
|
||||
|
||||
mkdir "$fromdir"
|
||||
mknod "$fromdir/char" c 42 69 || test_skipped "Can't create char device node?"
|
||||
mknod "$fromdir/block" b 42 69 || test_skipped "Can't create block device node?"
|
||||
|
||||
checkit "rsync -aHvv \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
|
||||
|
||||
exit 0
|
||||
# last [] may have failed but if we get here then we've won
|
||||
|
||||
@@ -150,7 +150,11 @@ checkit() {
|
||||
echo "-------------"
|
||||
echo "check how the files compare with diff:"
|
||||
echo ""
|
||||
diff -cr $2 $3 || failed=YES
|
||||
for f in `cd "$2"; find . -type f -print `
|
||||
do
|
||||
diff -u "$2"/"$f" "$3"/"$f" || failed=YES
|
||||
done
|
||||
|
||||
echo "-------------"
|
||||
echo "check how the directory listings compare with diff:"
|
||||
echo ""
|
||||
@@ -222,6 +226,11 @@ test_fail() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
test_skipped() {
|
||||
echo "$@" >&2
|
||||
exit 77
|
||||
}
|
||||
|
||||
# It failed, but we expected that. don't dump out error logs,
|
||||
# because most users won't want to see them. But do leave
|
||||
# the working directory around.
|
||||
|
||||
431
util.c
431
util.c
@@ -1,6 +1,8 @@
|
||||
/*
|
||||
Copyright (C) Andrew Tridgell 1996
|
||||
Copyright (C) Paul Mackerras 1996
|
||||
/* -*- c-file-style: "linux" -*-
|
||||
|
||||
Copyright (C) 1996-2000 by Andrew Tridgell
|
||||
Copyright (C) Paul Mackerras 1996
|
||||
Copyright (C) 2001, 2002 by Martin Pool <mbp@samba.org>
|
||||
|
||||
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
|
||||
@@ -26,102 +28,167 @@
|
||||
|
||||
extern int verbose;
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
Set a fd into nonblocking mode. Uses POSIX O_NONBLOCK if available,
|
||||
else
|
||||
if SYSV use O_NDELAY
|
||||
if BSD use FNDELAY
|
||||
Set a fd into nonblocking mode
|
||||
****************************************************************************/
|
||||
int set_nonblocking(int fd)
|
||||
void set_nonblocking(int fd)
|
||||
{
|
||||
int val;
|
||||
#ifdef O_NONBLOCK
|
||||
#define FLAG_TO_SET O_NONBLOCK
|
||||
#else
|
||||
#ifdef SYSV
|
||||
#define FLAG_TO_SET O_NDELAY
|
||||
#else /* BSD */
|
||||
#define FLAG_TO_SET FNDELAY
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
if((val = fcntl(fd, F_GETFL, 0)) == -1)
|
||||
return -1;
|
||||
val |= FLAG_TO_SET;
|
||||
return fcntl( fd, F_SETFL, val);
|
||||
#undef FLAG_TO_SET
|
||||
return;
|
||||
if (!(val & NONBLOCK_FLAG)) {
|
||||
val |= NONBLOCK_FLAG;
|
||||
fcntl(fd, F_SETFL, val);
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Set a fd into blocking mode
|
||||
****************************************************************************/
|
||||
void set_blocking(int fd)
|
||||
{
|
||||
int val;
|
||||
|
||||
if((val = fcntl(fd, F_GETFL, 0)) == -1)
|
||||
return;
|
||||
if (val & NONBLOCK_FLAG) {
|
||||
val &= ~NONBLOCK_FLAG;
|
||||
fcntl(fd, F_SETFL, val);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* this is taken from CVS */
|
||||
int piped_child(char **command,int *f_in,int *f_out)
|
||||
/* create a file descriptor pair - like pipe() but use socketpair if
|
||||
possible (because of blocking issues on pipes)
|
||||
|
||||
always set non-blocking
|
||||
*/
|
||||
int fd_pair(int fd[2])
|
||||
{
|
||||
int pid;
|
||||
int to_child_pipe[2];
|
||||
int from_child_pipe[2];
|
||||
int ret;
|
||||
|
||||
if (pipe(to_child_pipe) < 0 ||
|
||||
pipe(from_child_pipe) < 0) {
|
||||
rprintf(FERROR,"pipe: %s\n",strerror(errno));
|
||||
exit_cleanup(RERR_IPC);
|
||||
}
|
||||
#if HAVE_SOCKETPAIR
|
||||
ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
|
||||
#else
|
||||
ret = pipe(fd);
|
||||
#endif
|
||||
|
||||
|
||||
pid = do_fork();
|
||||
if (pid < 0) {
|
||||
rprintf(FERROR,"fork: %s\n",strerror(errno));
|
||||
exit_cleanup(RERR_IPC);
|
||||
}
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
extern int orig_umask;
|
||||
if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 ||
|
||||
close(to_child_pipe[1]) < 0 ||
|
||||
close(from_child_pipe[0]) < 0 ||
|
||||
dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
|
||||
rprintf(FERROR,"Failed to dup/close : %s\n",strerror(errno));
|
||||
exit_cleanup(RERR_IPC);
|
||||
}
|
||||
if (to_child_pipe[0] != STDIN_FILENO) close(to_child_pipe[0]);
|
||||
if (from_child_pipe[1] != STDOUT_FILENO) close(from_child_pipe[1]);
|
||||
umask(orig_umask);
|
||||
execvp(command[0], command);
|
||||
rprintf(FERROR,"Failed to exec %s : %s\n",
|
||||
command[0],strerror(errno));
|
||||
exit_cleanup(RERR_IPC);
|
||||
}
|
||||
|
||||
if (close(from_child_pipe[1]) < 0 ||
|
||||
close(to_child_pipe[0]) < 0) {
|
||||
rprintf(FERROR,"Failed to close : %s\n",strerror(errno));
|
||||
exit_cleanup(RERR_IPC);
|
||||
}
|
||||
|
||||
*f_in = from_child_pipe[0];
|
||||
*f_out = to_child_pipe[1];
|
||||
|
||||
set_nonblocking(*f_in);
|
||||
set_nonblocking(*f_out);
|
||||
|
||||
return pid;
|
||||
if (ret == 0) {
|
||||
set_nonblocking(fd[0]);
|
||||
set_nonblocking(fd[1]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int local_child(int argc, char **argv,int *f_in,int *f_out)
|
||||
|
||||
void print_child_argv(char **cmd)
|
||||
{
|
||||
int pid;
|
||||
rprintf(FINFO, RSYNC_NAME ": open connection using ");
|
||||
for (; *cmd; cmd++) {
|
||||
/* Look for characters that ought to be quoted. This
|
||||
* is not a great quoting algorithm, but it's
|
||||
* sufficient for a log message. */
|
||||
if (strspn(*cmd, "abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"0123456789"
|
||||
",.-_=+@/") != strlen(*cmd)) {
|
||||
rprintf(FINFO, "\"%s\" ", *cmd);
|
||||
} else {
|
||||
rprintf(FINFO, "%s ", *cmd);
|
||||
}
|
||||
}
|
||||
rprintf(FINFO, "\n");
|
||||
}
|
||||
|
||||
|
||||
/* this is derived from CVS code
|
||||
|
||||
note that in the child STDIN is set to blocking and STDOUT
|
||||
is set to non-blocking. This is necessary as rsh relies on stdin being blocking
|
||||
and ssh relies on stdout being non-blocking
|
||||
|
||||
if blocking_io is set then use blocking io on both fds. That can be
|
||||
used to cope with badly broken rsh implementations like the one on
|
||||
solaris.
|
||||
*/
|
||||
pid_t piped_child(char **command, int *f_in, int *f_out)
|
||||
{
|
||||
pid_t pid;
|
||||
int to_child_pipe[2];
|
||||
int from_child_pipe[2];
|
||||
extern int blocking_io;
|
||||
|
||||
if (verbose > 0) {
|
||||
print_child_argv(command);
|
||||
}
|
||||
|
||||
if (pipe(to_child_pipe) < 0 ||
|
||||
pipe(from_child_pipe) < 0) {
|
||||
if (fd_pair(to_child_pipe) < 0 || fd_pair(from_child_pipe) < 0) {
|
||||
rprintf(FERROR, "pipe: %s\n", strerror(errno));
|
||||
exit_cleanup(RERR_IPC);
|
||||
}
|
||||
|
||||
|
||||
pid = do_fork();
|
||||
if (pid == -1) {
|
||||
rprintf(FERROR, "fork: %s\n", strerror(errno));
|
||||
exit_cleanup(RERR_IPC);
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
extern int orig_umask;
|
||||
if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 ||
|
||||
close(to_child_pipe[1]) < 0 ||
|
||||
close(from_child_pipe[0]) < 0 ||
|
||||
dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
|
||||
rprintf(FERROR, "Failed to dup/close : %s\n",
|
||||
strerror(errno));
|
||||
exit_cleanup(RERR_IPC);
|
||||
}
|
||||
if (to_child_pipe[0] != STDIN_FILENO)
|
||||
close(to_child_pipe[0]);
|
||||
if (from_child_pipe[1] != STDOUT_FILENO)
|
||||
close(from_child_pipe[1]);
|
||||
umask(orig_umask);
|
||||
set_blocking(STDIN_FILENO);
|
||||
if (blocking_io) {
|
||||
set_blocking(STDOUT_FILENO);
|
||||
}
|
||||
execvp(command[0], command);
|
||||
rprintf(FERROR, "Failed to exec %s : %s\n",
|
||||
command[0], strerror(errno));
|
||||
exit_cleanup(RERR_IPC);
|
||||
}
|
||||
|
||||
if (close(from_child_pipe[1]) < 0 || close(to_child_pipe[0]) < 0) {
|
||||
rprintf(FERROR, "Failed to close : %s\n", strerror(errno));
|
||||
exit_cleanup(RERR_IPC);
|
||||
}
|
||||
|
||||
*f_in = from_child_pipe[0];
|
||||
*f_out = to_child_pipe[1];
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
pid_t local_child(int argc, char **argv,int *f_in,int *f_out)
|
||||
{
|
||||
pid_t pid;
|
||||
int to_child_pipe[2];
|
||||
int from_child_pipe[2];
|
||||
extern int read_batch; /* dw */
|
||||
|
||||
if (fd_pair(to_child_pipe) < 0 ||
|
||||
fd_pair(from_child_pipe) < 0) {
|
||||
rprintf(FERROR,"pipe: %s\n",strerror(errno));
|
||||
exit_cleanup(RERR_IPC);
|
||||
}
|
||||
|
||||
|
||||
pid = do_fork();
|
||||
if (pid < 0) {
|
||||
if (pid == -1) {
|
||||
rprintf(FERROR,"fork: %s\n",strerror(errno));
|
||||
exit_cleanup(RERR_IPC);
|
||||
}
|
||||
@@ -130,7 +197,7 @@ int local_child(int argc, char **argv,int *f_in,int *f_out)
|
||||
extern int am_sender;
|
||||
extern int am_server;
|
||||
|
||||
am_sender = !am_sender;
|
||||
am_sender = read_batch ? 0 : !am_sender;
|
||||
am_server = 1;
|
||||
|
||||
if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 ||
|
||||
@@ -397,17 +464,6 @@ int robust_rename(char *from, char *to)
|
||||
return -1;
|
||||
return do_rename(from, to);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* sleep for a while via select */
|
||||
void u_sleep(int usec)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = usec;
|
||||
select(0, NULL, NULL, NULL, &tv);
|
||||
}
|
||||
|
||||
|
||||
@@ -503,7 +559,7 @@ static void glob_expand_one(char *s, char **argv, int *argc, int maxargs)
|
||||
globfree(&globbuf);
|
||||
return;
|
||||
}
|
||||
for (i=0; i<(maxargs - (*argc)) && i<globbuf.gl_pathc;i++) {
|
||||
for (i=0; i<(maxargs - (*argc)) && i < (int) globbuf.gl_pathc;i++) {
|
||||
if (i == 0) free(argv[*argc]);
|
||||
argv[(*argc) + i] = strdup(globbuf.gl_pathv[i]);
|
||||
if (!argv[(*argc) + i]) out_of_memory("glob_expand");
|
||||
@@ -528,10 +584,7 @@ void glob_expand(char *base1, char **argv, int *argc, int maxargs)
|
||||
s = strdup(s);
|
||||
if (!s) out_of_memory("glob_expand");
|
||||
|
||||
base = (char *)malloc(strlen(base1)+3);
|
||||
if (!base) out_of_memory("glob_expand");
|
||||
|
||||
sprintf(base," %s/", base1);
|
||||
if (asprintf(&base," %s/", base1) <= 0) out_of_memory("glob_expand");
|
||||
|
||||
q = s;
|
||||
while ((p = strstr(q,base)) && ((*argc) < maxargs)) {
|
||||
@@ -558,33 +611,6 @@ void strlower(char *s)
|
||||
}
|
||||
}
|
||||
|
||||
/* this is like vsnprintf but it always null terminates, so you
|
||||
can fit at most n-1 chars in */
|
||||
int vslprintf(char *str, int n, const char *format, va_list ap)
|
||||
{
|
||||
int ret = vsnprintf(str, n, format, ap);
|
||||
if (ret >= n || ret < 0) {
|
||||
str[n-1] = 0;
|
||||
return -1;
|
||||
}
|
||||
str[ret] = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* like snprintf but always null terminates */
|
||||
int slprintf(char *str, int n, char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int ret;
|
||||
|
||||
va_start(ap, format);
|
||||
ret = vslprintf(str,n,format,ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void *Realloc(void *p, int size)
|
||||
{
|
||||
if (!p) return (void *)malloc(size);
|
||||
@@ -806,28 +832,91 @@ int u_strcmp(const char *cs1, const char *cs2)
|
||||
return (int)*s1 - (int)*s2;
|
||||
}
|
||||
|
||||
static OFF_T last_ofs;
|
||||
static OFF_T last_ofs;
|
||||
static struct timeval print_time;
|
||||
static struct timeval start_time;
|
||||
static OFF_T start_ofs;
|
||||
|
||||
void end_progress(void)
|
||||
static unsigned long msdiff(struct timeval *t1, struct timeval *t2)
|
||||
{
|
||||
return (t2->tv_sec - t1->tv_sec) * 1000
|
||||
+ (t2->tv_usec - t1->tv_usec) / 1000;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param ofs Current position in file
|
||||
* @param size Total size of file
|
||||
* @param is_last True if this is the last time progress will be
|
||||
* printed for this file, so we should output a newline. (Not
|
||||
* necessarily the same as all bytes being received.)
|
||||
**/
|
||||
static void rprint_progress(OFF_T ofs, OFF_T size, struct timeval *now,
|
||||
int is_last)
|
||||
{
|
||||
int pct = (ofs == size) ? 100 : (int)((100.0*ofs)/size);
|
||||
unsigned long diff = msdiff(&start_time, now);
|
||||
double rate = diff ? (double) (ofs-start_ofs) * 1000.0 / diff / 1024.0 : 0;
|
||||
const char *units;
|
||||
double remain = rate ? (double) (size-ofs) / rate / 1000.0: 0.0;
|
||||
int remain_h, remain_m, remain_s;
|
||||
|
||||
if (rate > 1024*1024) {
|
||||
rate /= 1024.0 * 1024.0;
|
||||
units = "GB/s";
|
||||
} else if (rate > 1024) {
|
||||
rate /= 1024.0;
|
||||
units = "MB/s";
|
||||
} else {
|
||||
units = "kB/s";
|
||||
}
|
||||
|
||||
remain_s = (int) remain % 60;
|
||||
remain_m = (int) (remain / 60.0) % 60;
|
||||
remain_h = (int) (remain / 3600.0);
|
||||
|
||||
rprintf(FINFO, "%12.0f %3d%% %7.2f%s %4d:%02d:%02d%s",
|
||||
(double) ofs, pct, rate, units,
|
||||
remain_h, remain_m, remain_s,
|
||||
is_last ? "\n" : "\r");
|
||||
}
|
||||
|
||||
void end_progress(OFF_T size)
|
||||
{
|
||||
extern int do_progress, am_server;
|
||||
|
||||
if (do_progress && !am_server) {
|
||||
rprintf(FINFO,"\n");
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
rprint_progress(size, size, &now, True);
|
||||
}
|
||||
last_ofs = 0;
|
||||
last_ofs = 0;
|
||||
start_ofs = 0;
|
||||
print_time.tv_sec = print_time.tv_usec = 0;
|
||||
start_time.tv_sec = start_time.tv_usec = 0;
|
||||
}
|
||||
|
||||
void show_progress(OFF_T ofs, OFF_T size)
|
||||
{
|
||||
extern int do_progress, am_server;
|
||||
struct timeval now;
|
||||
|
||||
if (do_progress && !am_server) {
|
||||
if (ofs > last_ofs + 1000) {
|
||||
int pct = (int)((100.0*ofs)/size);
|
||||
rprintf(FINFO,"%.0f (%d%%)\r", (double)ofs, pct);
|
||||
last_ofs = ofs;
|
||||
}
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
if (!start_time.tv_sec && !start_time.tv_usec) {
|
||||
start_time.tv_sec = now.tv_sec;
|
||||
start_time.tv_usec = now.tv_usec;
|
||||
start_ofs = ofs;
|
||||
}
|
||||
|
||||
if (do_progress
|
||||
&& !am_server
|
||||
&& ofs > last_ofs + 1000
|
||||
&& msdiff(&print_time, &now) > 250) {
|
||||
rprint_progress(ofs, size, &now, False);
|
||||
last_ofs = ofs;
|
||||
print_time.tv_sec = now.tv_sec;
|
||||
print_time.tv_usec = now.tv_usec;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -900,3 +989,85 @@ char *timestring(time_t t)
|
||||
return(TimeBuf);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sleep for a specified number of milliseconds.
|
||||
*
|
||||
* Always returns TRUE. (In the future it might return FALSE if
|
||||
* interrupted.)
|
||||
**/
|
||||
int msleep(int t)
|
||||
{
|
||||
int tdiff=0;
|
||||
struct timeval tval,t1,t2;
|
||||
|
||||
gettimeofday(&t1, NULL);
|
||||
gettimeofday(&t2, NULL);
|
||||
|
||||
while (tdiff < t) {
|
||||
tval.tv_sec = (t-tdiff)/1000;
|
||||
tval.tv_usec = 1000*((t-tdiff)%1000);
|
||||
|
||||
errno = 0;
|
||||
select(0,NULL,NULL, NULL, &tval);
|
||||
|
||||
gettimeofday(&t2, NULL);
|
||||
tdiff = (t2.tv_sec - t1.tv_sec)*1000 +
|
||||
(t2.tv_usec - t1.tv_usec)/1000;
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
Determine if two file modification times are equivalent (either exact
|
||||
or in the modification timestamp window established by --modify-window)
|
||||
Returns 0 if the times should be treated as the same, 1 if the
|
||||
first is later and -1 if the 2nd is later
|
||||
*******************************************************************/
|
||||
int cmp_modtime(time_t file1, time_t file2)
|
||||
{
|
||||
extern int modify_window;
|
||||
|
||||
if (file2 > file1) {
|
||||
if (file2 - file1 <= modify_window) return 0;
|
||||
return -1;
|
||||
}
|
||||
if (file1 - file2 <= modify_window) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __INSURE__XX
|
||||
#include <dlfcn.h>
|
||||
|
||||
/*******************************************************************
|
||||
This routine is a trick to immediately catch errors when debugging
|
||||
with insure. A xterm with a gdb is popped up when insure catches
|
||||
a error. It is Linux specific.
|
||||
********************************************************************/
|
||||
int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6)
|
||||
{
|
||||
static int (*fn)();
|
||||
int ret;
|
||||
char *cmd;
|
||||
|
||||
asprintf(&cmd, "/usr/X11R6/bin/xterm -display :0 -T Panic -n Panic -e /bin/sh -c 'cat /tmp/ierrs.*.%d ; gdb /proc/%d/exe %d'",
|
||||
getpid(), getpid(), getpid());
|
||||
|
||||
if (!fn) {
|
||||
static void *h;
|
||||
h = dlopen("/usr/local/parasoft/insure++lite/lib.linux2/libinsure.so", RTLD_LAZY);
|
||||
fn = dlsym(h, "_Insure_trap_error");
|
||||
}
|
||||
|
||||
ret = fn(a1, a2, a3, a4, a5, a6);
|
||||
|
||||
system(cmd);
|
||||
|
||||
free(cmd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user