/* * Copyright status: public domain. See the end of this block comment. * * compare - compare file hierarchies * * Compilation notes: * -DNO_PROGNAME * For use on systems without __progname: provides its own * __progname, obtained from argv[0]. * -DNO_NETWORK * Completely suppress all network-interface code (and * renders the next two defines irrelevant). This is * intended for systems with broken networking API * support, but it can also be used on a normal system to * produce a binary without networking functionality. * (This is not to say that such a binary cannot be used * for a network compare, just that compare itself will * not handle the networking.) This also disables -trace, * because the implementation uses some networking calls. * -DNO_GETINFO * Provide getnameinfo() and getaddrinfo() implementations * which just call on inet_aton, gethostbyname, etc. * Intended for use on pre-IPv6 machines. The provided * versions are just barely sufficient for use here; they * are not general-purpose implementations (for example, * they don't bother implementing some facilities not used * by compare). By default, this also turns on * -DNO_SOCKADDR_STORAGE (see -DHAS_SOCKADDR_STORAGE). * -DNO_SOCKADDR_STORAGE * For systems without struct sockaddr_storage. This is * the default if -DNO_GETINFO is used. * -DHAS_SOCKADDR_STORAGE * For systems with struct sockaddr_storage. This is the * default if -DNO_GETINFO is not used. * -DNO_INET_ATON * Provide a rudimentary inet_aton() that uses inet_addr * (and thus doesn't work right for 255.255.255.255). * Intended for use with -DNO_GETINFO on _very_ old * systems. * -DBROKEN_ARPA_INET_H * For systems whose is broken and produces * errors when it's included without being * included earlier. * -DDECLARE_ALARM * For systems that don't declare alarm() in their include * files and thus need us to declare it. * * Usage: * compare [flags] [[user@]machine[!program]:]directory * [[user@]machine[!program]:]directory ... * * flags (old forms still accepted) * -update (old form: -u) * -follow (old form: -h) * -sgid-dirs (old form: -g) * -mtimes (old form: -t) * -no-owners (old form: -o) * -encode (old form: -x) * -no-sparse * -trace * -prune * -prunewild * -prunebase * -prunex * -md5 * -gzip * -rsync * -no-delete * -rsh * -dir * -mask * -modemod * -tick * -links * -ignore-nonexistent (also -inx) (applies to next directory) * -readonly (also -ro) * -check * -ignore-sockets * -map from to * * -R * -accept * -connect
* -force * -bg * -limit * * Compare compares directories, possibly on different machines, with * one another. * * Each (non-option) argument specifies a directory. If no :s occur in * the argument, or if a slash appears before the first : or !, the * directory is on the local machine. Otherwise, the portion before * the : specifies a machine name with optional user and program * specifications. By default, compare uses ssh(1), which must be in * the path, to run compare on the remote machine; see the -rsh option * for a way to change this. If a username is given with an @ before * the machine name, it will be used with ssh's -l option to specify * the remote username. If a program name is specified after the * machine name with a !, that will be the name used to run compare on * the remote machine. The default for the username is whatever ssh * feels like using (normally the local username); the default for the * program name is the argv[0] value for the master run. If the machine * name begins with a [, then the machine name consists of everything up * to the next ], with the [ ] stripped, regardless of what characters * may be contained between them. A [ is not special unless it's the * first character of the machine name. This introduces some * ambiguities and error conditions: * - If the argument contains a colon, and the first character * after the @ (or the first character, if there's no @) is a [, * but there's no ], this is an error. * - If likewise, but there's an ], and the next character isn't a * :, this is an error. * - Neither of the above applies if there's a / before the [. * * If the machine name is an empty string (ie, the argument begins with * a colon), the argument is taken to be of the form * :keyword:info * to specify an alternative way of contacting the remote. Currently, * the only defined ways are * :connect:address:port-number:directory * and * :accept:port-number:directory * As with machine names, if "address" begins with a [, it consists of * everything up to the next ], with the [ ] stripped, regardless of what * characters that may include. * * These cause compare to do a connect or a listen/accept with the given * port number (and address, for "connect") to establish the connection * to the remote compare process. This is useful when you have shell * access on two machines but can't (or don't want to) allow ssh-style * access between them. See the -R, -accept, and -connect options for * more. Note that certain options must normally agree between the * master and the remotes, either given to both or given to neither. * These options are -mtimes, -encode, and -links. In addition, -gzip * must be given to the remote if it's given to the master, though the * converse is not true (if the master is not given -gzip, it will not * matter whether the remotes are or not). Other flags (notably -follow, * -no-owners, and -sgid-dirs) may usefully vary between the master and * the remotes; some others (-update, for example) affect only the * master and have no effect when given to a remote. * * -dir takes its following argument as a directory to compare. It * exists to allow specifying a directory that begins, or may begin, * with a -. * * -update means to update: the first-named directory is treated as a * master copy and all others are made identical to it, like a * super-picky rdist (compare compares more things than rdist does). * * -follow means to follow symlinks instead of checking the links * themselves for matching. * * -sgid-dirs says to ignore the set-group-ID bits on directories when * comparing modes and to leave them alone when setting modes. It's * like a variant of "-mask 5777" that applies only to directories. * * -mtimes says that modification times are important and should be * considered when comparing for differences and preserved when * updating. * * -no-owners says that the owning UID and GID values for files are * unimportant and should not be set or compared. * * -encode says that network communication should be passed through a * simple binary-to-printable filter. (This _should_ never be needed, * but the world is far from ideal, and it does seem to help in some * cases.) * * -no-sparse says that files should never be created sparse. Without * this, files compare creates will always be as sparse as possible. * * -trace says to trace data sent and received between the master * process and the auxiliaries. (If -encode is in effect, this traces * the encoded form as well.) Trace output is sent to stderr. * * -prune says that anything called (which should be a * path relative to the directories being compared) should be ignored * when found: neither it nor anything under it should be compared, * updated, or otherwise touched. compare behaves as if no such names * existed anywhere, except that if a directory is copied wholesale * because it was nonexistent during an update operation, -prune * entries referring to things inside that directory will not limit * what is copied. * * -prunewild is just like -prune except that the argument is * a globbing pattern rather than a simple name. * * -prunebase is just like -prunewild except that the * argument is matched against just the last component of the name in * question rather than the entire name. * * -prunex says that that the sense of the tests used for pruning * should be reversed: everything is pruned _except_ the things named * with -prune (and similar) arguments. * * -md5 says that when plain files contents' need to be compared, * rather than comparing the full contents, simply compute an md5 * checksum of each file, and assume that matching checksums means * matching contents. (This is useful when running over a network * link sufficiently slow that it takes longer to send the file than * it does to compute its checksum, and you're willing to take the * slight risk that different files will checksum the same.) * * -gzip says that when copying files from one place to another, the * files are to be gzipped at the point of reading and gunzipped at * the point of writing. (The intention is to reduce bytes sent over * the net; this is intended for use across slow links, where cpu * cycles are cheaper than network bytes.) The argument is passed to * gzip; it is expected to be something like --best or --fast. * * -rsync says that when copying files, compare should use an algorithm * akin to rsync(1)'s. * * -no-delete says that an update operation (see -update) should ignore * files that exist on the remote but not the master. (Normally, such * files would be deleted.) * * -rsh says to use the specified instead of ssh * for running remote programs. This is not a global option; it * affects directory specifications to the right of it (until another * -rsh option), but not those to the left of it. The is * run as either * machine-name command [args...] * or * machine-name -l username command [args...] * where machine-name, username, and command are as described above, * and args is zero or more arguments to be passed to the remote * executable's command line. * * -mask specifies what bits to pay attention to when comparing * permissions. Both objects' mode bits are ANDed with the -mask * argument before comparison. The argument is an octal number. (The * mode bits are not modified; if (for example) a file's contents * differ, the mode bits printed will be the actual mode bits, not * masked by the -mask argument.) * * -modemod specifies that when -update is in effect, all mode bits * read from the master directory tree are to be modified before * comparing, printing, or setting on the other directory trees. * Everything is ANDed with the argument and then ORed with the * argument. (Only the 07777 bits in each argument are * significant.) When -update is not in effect, all mode bits read * from any directory tree are modified as described before comparing * or printing. * * -tick gives an interval in seconds; every this many seconds, the * current path being worked on will be printed. (Ticks occurring * before all remotes start are ignored.) A tick can also be forced * by explicitly sending a SIGALRM, either from the command line via * kill(1) or programmatically with kill(2), whether or not -tick was * given. (The signal must be sent to the parent compare process, * which almost always means the lowest PID.) This output, like that * generated by SIGINFO, goes to /dev/tty. If /dev/tty can't be * opened at startup, SIGINFO and SIGALRM output is discarded. * * -links causes compare to notice files that are linked together. The * actual link count is ignored, except that compare doesn't bother * doing this for files with link count 1; this is necessary because * it's possible that there are links which are not visible in the * subtree being compared. Differences in linking patterns are * considered differences, and, with -update, are fixed. This is * necessary when using -update to update directories with different * link patterns to avoid stuttering. Consider a master directory * containing foo/file and bar/file, with identical contents but * different permissions, and a replica directory containing foo/file * and bar/file hardlinked together. An update will chmod the file on * the slave if necessary, to make it match the master's bar/file, * when bar/ is scanned, then chmod it again, to match the master's * foo/file, when foo/ is scanned; there will always be a difference * until the hardlinking on the replica is undone - or a run with * -links is done to fix the linking. (Note that this is not done for * directories.) * * -ignore-nonexistent (which can also be spelled -inx) specifies that * the next directory argument is to be treated slightly differently: * if the only reason to print a difference report is that an entry is * nonexistent in one or more so-marked directories, then the report * is not printed. This option should not be used with -u; the result * is "whatever the implementation gives you", not anything * well-specified. * * -check is designed to verify that all directories match; if they do, * exit status is 0, if not, 1. No output is produced for * differences, nor does compare progress past the first difference * discovered. -check is useful only on the master run; it is not * passed automatically to remotes, and remotes ignore it if it's * given on a run-by-hand remote. * * -ignore-sockets tells compare to ignore sockets. When sockets are * found in any of the directories being compared, the effect is * almost as if they didn't exist at all; the only difference is that * when messages are printed, sockets are described as such, rather * than being described as nonexistent. But, for example, a socket * existing in one directory and the name being nonexistent in another * will not, in itself, cause a mismatch. But a socket in the master * directory of an update operation will cause a non-socket in a * non-master directory to be deleted. -ignore-sockets matters only * on the master run. * * -readonly is used with -R; it specifies that the remote is to reject * any commands which would modify anything. * * -R says that this compare process is a remote. It is normally not * needed unless you're using explicit rendezvous points instead of * the usual ssh way of starting the remotes. * * -accept is useful only with -R. It specifies that the process is to * listen/accept on the given port number to establish the connection * to the master, instead of assuming the connection is already * present on stdin and stdout. -R -accept is used when the master * uses a :connect:-style remote specifier. * * -connect is useful only with -R. It specifies that the process is * to connect to the given port number at the given address to * establish the connection to the master, instead of assuming the * connection is already present on stdin and stdout. -R -connect is * used when the master uses a :accept:-style remote specifier. * * -force is useful only with -R. It specifies that the remote is to * ignore the directory that is specified at protocol startup, using * the -force argument instead. * * -bg is useful only with -R. It specifies that the remote is to * background itself as soon as any initial setup (such as listening * for -accept) is completed. * * -limit is useful only with -R. It specifies that the remote is to * refuse to accept a master-specified path that begins with a slash * or attempts to dot-dot its way up the directory tree. * * -map converts "from" to "to" if it is found when reading the * directory (applies to the next directory only). Using this with * -update works sanely only when the directory it is applied to is * the master directory. * * No provision is made for user names containing /s, @s, or :s or * program names containing :s. Machine names can contain any * character except ], provided they're written []-bracketed. * * Copyright status: this program is entirely my work, and I explicitly * place it in the public domain. Anyone may use it in any way for * any purpose (though I would appreciate credit where it is due). * I'd also be interested in hearing about your experiences with it, * especially if you think you've run into a bug. * * /~\ The ASCII der Mouse * \ / Ribbon Campaign * X Against HTML mouse@rodents.montreal.qc.ca * / \ Email! 7D C8 61 52 5D E7 2D 39 4E F1 31 3E E8 B3 27 4B */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef NO_NETWORK #ifdef BROKEN_ARPA_INET_H #include #endif #include #include #include #include #include #endif extern const char *__progname; /* Part of the Linux fixup, below. */ /* Use this only in contexts where the name length is known to be >= n */ #define DLEN_IS(d,n) ((d)->d_namlen == (n)) /* fixup for SunOS versions which have and poll() but not these */ #ifndef POLLRDNORM #define POLLRDNORM 0 #endif #ifndef POLLRDBAND #define POLLRDBAND 0 #endif #ifndef POLLPRI #define POLLPRI 0 #endif #ifndef INFTIM #define INFTIM (-1) #endif /* fixup for SunOS, which is without lchmod/lchown/lutimes and strtoul */ #if defined(sparc) && defined(sun) /*&& defined(unix)*/ #define lchmod(x,y) ((errno=EINVAL),-1) #define lchown(x,y,z) ((errno=EINVAL),-1) #define lutimes(x,y) ((errno=EINVAL),-1) #define strtoul(s,e,b) ((unsigned long int)strtol(s,e,b)) #endif /* * Fixup for Linux. There are multiple issues here. * * - Missing d_namlen. This is why DLEN_IS (see above) exists. * * - isn't enough for time-based stuff - it demands * . * * - lchmod is missing - but only in some versions; versions that have * it throw an error if we naïvely supply a dummy version. I'd like * to use it if it exists, but haven't found a good way, so I provide * a dummy version and a dance to shut up the errors. * * - pread/pwrite. Some versions don't have it; others have it but * demand a conformance-level macro defined before including * . So we do emulation. * * - _GNU_SOURCE must be defined before including to get * asprintf. However, it simply doesn't work to do that now, * presumably because stdio.h has already been included. So, ugly as * it is, we provide our own declaration. */ #ifdef __linux__ extern int asprintf(char **, const char *, ...) __attribute__((__format__(__printf__,2,3))); #include #undef DLEN_IS #define DLEN_IS(d,n) ((d)->d_name[(n)] == '\0') #include #undef lchmod #define lchmod compare_lchmod static int lchmod(const char *path, int mode) { struct stat stb; if ((stat(path,&stb) == 0) && ((stb.st_mode & S_IFMT) == S_IFLNK)) { errno = EINVAL; return(-1); } return(chmod(path,mode)); } #undef pread #define pread compare_pread static size_t pread(int fd, void *buf, size_t len, off_t off) { off_t oldoff; int e; size_t rv; oldoff = lseek(fd,0,SEEK_CUR); if (lseek(fd,off,SEEK_SET) < 0) { rv = -1; } else { rv = read(fd,buf,len); } e = errno; lseek(fd,oldoff,SEEK_SET); errno = e; return(rv); } #undef pwrite #define pwrite compare_pwrite static size_t pwrite(int fd, const void *buf, size_t len, off_t off) { off_t oldoff; int e; size_t rv; oldoff = lseek(fd,0,SEEK_CUR); if (lseek(fd,off,SEEK_SET) < 0) { rv = -1; } else { rv = write(fd,buf,len); } e = errno; lseek(fd,oldoff,SEEK_SET); errno = e; return(rv); } #endif #define CURRENT_VERSION 2 #define RSYNC_BLKSIZE 1009 #define RSYNC_POLY 0x82f63b78 #define RSYNC_HTSIZE 65537 #ifdef NO_PROGNAME const char *__progname; int main(int, char **); int main_(int, char **); int main(int ac, char **av) { __progname = av[0]; return(main_(ac,av)); } #define main main_ #endif #if !defined(NO_SOCKADDR_STORAGE) && !defined(HAS_SOCKADDR_STORAGE) #ifdef NO_GETINFO #define NO_SOCKADDR_STORAGE #else #define HAS_SOCKADDR_STORAGE #endif #endif #ifndef NO_NETWORK #ifdef NO_GETINFO struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; size_t ai_addrlen; char *ai_canonname; struct sockaddr *ai_addr; struct addrinfo *ai_next; } ; #define EAI_NODATA 10001 #define EAI_SERVICE 10002 #define AI_PASSIVE 0x00000001 static void gai_add_entry(struct addrinfo **rvp, struct in_addr *iap, int pno) { struct addrinfo *ai; struct sockaddr_in *sin; sin = malloc(sizeof(*sin)); bzero(sin,sizeof(*sin)); /* Don't set sin->sin_size; the few systems that need NO_GETINFO but have sin_size can deal with a zero sin_size. */ sin->sin_family = AF_INET; sin->sin_addr = *iap; sin->sin_port = htons(pno); ai = malloc(sizeof(*ai)); ai->ai_flags = 0; ai->ai_family = AF_INET; ai->ai_socktype = SOCK_STREAM; ai->ai_protocol = IPPROTO_TCP; ai->ai_addrlen = sizeof(struct sockaddr_in); ai->ai_canonname = 0; ai->ai_addr = (void *) sin; ai->ai_next = *rvp; *rvp = ai; } int getaddrinfo(const char *, const char *, const struct addrinfo *, struct addrinfo **); int getaddrinfo(const char *h, const char *s, const struct addrinfo *hints, struct addrinfo **rvp) { unsigned long int pno; char *se; struct addrinfo *rv; struct in_addr ia; rv = 0; pno = strtoul(s,&se,0); if ((s == se) || *se) { struct servent *serv; serv = getservbyname(s,"tcp"); if (serv == 0) return(EAI_SERVICE); pno = ntohs(serv->s_port); } else if (pno > 65535) { return(EAI_SERVICE); } if (h) { if (inet_aton(h,&ia)) { gai_add_entry(&rv,&ia,pno); } else { struct hostent *host; int i; host = gethostbyname(h); if ( !host || !host->h_addr_list || (host->h_addrtype != AF_INET) || (host->h_length != sizeof(struct sockaddr_in)) ) return(EAI_NODATA); #define HAL_SIN(h,i) ((struct sockaddr_in *)(h)->h_addr_list[(i)]) for (i=0;host->h_addr_list[i];i++) { if (HAL_SIN(host,i)->sin_family == AF_INET) gai_add_entry(&rv,&HAL_SIN(host,i)->sin_addr,pno); } #undef HAL_SIN } } else { ia.s_addr = (hints->ai_flags & AI_PASSIVE) ? INADDR_ANY : htonl(0x7f000001); gai_add_entry(&rv,&ia,pno); } *rvp = rv; return(rv?0:EAI_NODATA); } void freeaddrinfo(struct addrinfo *); void freeaddrinfo(struct addrinfo *ai) { struct addrinfo *ai2; while (ai) { free(ai->ai_addr); ai2 = ai->ai_next; free(ai); ai = ai2; } } const char *gai_strerror(int); const char *gai_strerror(int ec) { switch (ec) { case EAI_NODATA: return("No address associated with nodename"); break; case EAI_SERVICE: return("Servname not known"); break; } return("Unknown error"); } #define NI_MAXHOST 32 #define NI_MAXSERV 32 #define NI_NUMERICHOST 1 #define NI_NUMERICSERV 2 int getnameinfo(const struct sockaddr *, socklen_t, char *, size_t, char *, size_t, int); int getnameinfo(const struct sockaddr *sa, socklen_t len, char *h, size_t hlen, char *s, size_t slen, int flags __attribute__((__unused__))) { if ((len != sizeof(struct sockaddr_in)) || (sa->sa_family != AF_INET)) { errno = EINVAL; return(-1); } if (h) { snprintf(h,hlen,"%s",inet_ntoa(((const struct sockaddr_in *)sa)->sin_addr)); } if (s) { snprintf(s,slen,"%u",(unsigned int)ntohs(((const struct sockaddr_in *)sa)->sin_port)); } return(0); } #endif #ifdef NO_INET_ATON int inet_aton(const char *, struct in_addr *); int inet_aton(const char *s, struct in_addr *ap) { unsigned long int a; a = inet_addr(s); if (a == INADDR_NONE) return(0); ap->s_addr = a; return(1); } #endif #endif #ifdef NO_SOCKADDR_STORAGE #define sockaddr_storage sockaddr_in #endif #ifdef DECLARE_ALARM extern void alarm(unsigned int); #endif #define NEW(t) ((t *)malloc(sizeof(t))) #define OLD(x) free((x)) /* code assumes WORKSIZE is a multiple of 512 */ #define WORKSIZE 10240 #define MAXREMOTES 64 #define COPYBLK_DATA 1 #define COPYBLK_HOLE 2 #ifdef NO_FIFO #undef S_IFIFO #endif static int flag_debug; static int flag_follow; static int flag_sgiddir; static int flag_noown; static int flag_remote; static int flag_mtimes; static int flag_update; static int flag_encode; #ifndef NO_NETWORK static int flag_trace; #endif static int flag_md5; static int flag_gzip; static int flag_rsync; static int flag_nosparse; static int flag_nodelete; static int flag_accept; static int flag_connect; static int flag_links; static int flag_nextinx; static int flag_readonly; static int flag_check; static int flag_bg; static int flag_limitslave; static int flag_ignoresockets; #ifndef NO_NETWORK static char *conn_port; static char *conn_addr; #endif static int perm_mask; static int perm_and = ~0; static int perm_or = 0; static int tick_seconds; #ifndef NO_NETWORK static int trace_ctl = -1; #endif static char *force_dir = 0; static void (*check_exit)(void); static char *defprogram; typedef struct mask MASK; typedef struct entry ENTRY; typedef struct entry_tconc ENTRY_TCONC; typedef struct remote REMOTE; typedef struct rstack RSTACK; typedef struct prune PRUNE; typedef struct mvlist MVLIST; typedef struct mapping MAPPING; typedef struct linktable LINKTABLE; typedef struct linkentry LINKENTRY; #define MASKLONGS ((MAXREMOTES+31)>>5) struct mask { unsigned long int bits[MASKLONGS]; } ; #if MASKLONGS == 1 #define MASK_ZERO(m) ((m).bits[0] = 0) #define MASK_SET(m,bit) ((m).bits[0] |= (1 << (bit))) #define MASK_CLR(m,bit) ((m).bits[0] &= ~(1 << (bit))) #define MASK_TST(m,bit) ((m).bits[0] & (1 << (bit))) #define MASK_ISZERO(m) ((m).bits[0] == 0) #define MASK_EQ(m1,m2) ((m1).bits[0] == (m2).bits[0]) #define MASK_ONEBIT(m) ((m).bits[0] && (((m).bits[0] & ((m).bits[0]-1)) == 0)) #else static const MASK zmask; #define MASK_ZERO(m) ((m) = zmask) #define MASK_SET(m,bit) ((m).bits[(bit)>>5] |= (1 << ((bit)&31))) #define MASK_CLR(m,bit) ((m).bits[(bit)>>5] &= ~(1 << ((bit)&31))) #define MASK_TST(m,bit) ((m).bits[(bit)>>5] & (1 << ((bit)&31))) #define MASK_ISZERO(m) mask_iszero(m) #define MASK_EQ(m1,m2) mask_eq(m1,m2) #define MASK_ONEBIT(m) mask_onebit(m) #endif #ifndef NO_NETWORK typedef struct acinfo ACINFO; struct acinfo { unsigned int type; #define ACT_ACCEPT 1 #define ACT_CONNECT 2 int *fdloc; union { struct { int nfds; int *fds; char **txt; } acc; struct { int fd; struct addrinfo *ai; char *txt; struct addrinfo *ai0; const char *astr; const char *pstr; } conn; } u; } ; #endif struct mapping { MAPPING *link; char *dir; char *from; char *to; } ; struct mvlist { MVLIST *link; MASK m; unsigned long long int v; } ; struct prune { PRUNE *link; unsigned int type; #define PT_PATH 1 #define PT_WILD 2 #define PT_BASE 3 char *path; } ; struct entry { ENTRY *link; char *name; int mode; int uid; int gid; long int mtime; int nlinks; unsigned long long int linkdev; unsigned long long int linkino; LINKENTRY *hardlink; int firstlink; unsigned long long int rdev_min; unsigned long long int rdev_maj; unsigned long long int size; char *softlink; MASK present; } ; struct entry_tconc { ENTRY *list; ENTRY **tail; } ; struct remote { REMOTE *link; char *fullarg; char *user; char *machine; char *program; char *directory; const char *rsh; int rfd; int wfd; unsigned char errored : 1; unsigned char ignnx : 1; #ifndef NO_NETWORK ACINFO *acinfo; #endif FILE *rf; FILE *wf; int bit; ENTRY *dirlist; MAPPING *mappings; LINKTABLE *links; int worklen; char work[WORKSIZE]; } ; struct linktable { LINKENTRY *links; } ; /* * The name field has somewhat overloaded semantics. Normally it is * the path of the first-encountered entity with this . But * it can also be nil, which means that this entry has conceptually * been removed from the table, because the entity that corresponded * to the name has been (or should have been) removed. */ struct linkentry { LINKENTRY *link; unsigned long long int dev; unsigned long long int ino; char *name; } ; struct rstack { RSTACK * volatile link; ENTRY * volatile list; ENTRY * volatile cur; } ; #define EACH_REM(var) var=remotes;var;var=var->link static const char *currsh = "ssh"; static REMOTE *remotes; static REMOTE *umaster; static MASK active; static const char *cur_dir; static char *cur_ent; static int cur_pruned; static MASK cur_nx; static int prunex; static int needhdg; static PRUNE *prunelist; static char *gzip_arg; static RSTACK * volatile rstack; static volatile int progress; #define MAX_PROGRESS 5 static REMOTE * volatile waitrem; static MAPPING *pendmap; static char slavebase[WORKSIZE]; static char slavework[WORKSIZE]; static char slavetemp[WORKSIZE]; static char slavetemp2[WORKSIZE]; static char zeroblk[WORKSIZE]; static ENTRY *freeentries; static __inline__ unsigned long int ullmin(unsigned long long int, unsigned long long int) __attribute__((__const__)); static __inline__ unsigned long int ullmin(unsigned long long int a, unsigned long long int b) { return((alink; } else { e = NEW(ENTRY); } e->name = 0; e->hardlink = 0; e->softlink = 0; return(e); } static void freeentry(ENTRY *e) { if (e->name) free(e->name); if (e->softlink) free(e->softlink); e->link = freeentries; freeentries = e; } static ENTRY *copyentry(ENTRY *e) { ENTRY *new; new = newentry(); *new = *e; if (e->name) new->name = strdup(e->name); if (e->softlink) new->softlink = strdup(e->softlink); return(new); } #if MASKLONGS > 1 static int mask_iszero(MASK m) { int i; for (i=0;i 1 static int mask_eq(MASK m1, MASK m2) { int i; for (i=0;i 1 static int mask_onebit(MASK m) { int i; for (i=0;i= MASKLONGS) return(0); if (m.bits[i] & (m.bits[i]-1)) return(0); for (i++;ibits[0] |= (b).bits[0]) #else static void mask_setor(MASK *a, MASK b) { int i; for (i=0;ibits[i] |= b.bits[i]; } #endif static MASK mask_and(MASK a, MASK b) { #if MASKLONGS == 1 a.bits[0] &= b.bits[0]; #else int i; for (i=0;ibits[0] &= ~(b).bits[0]) #else static void mask_setandnot(MASK *a, MASK b) { int i; for (i=0;ibits[i] &= ~b.bits[i]; } #endif static void bin2hex(unsigned char *bin, unsigned char *hex, int n) { for (;n>0;n--) { *hex++ = 48 + ((*bin)>>4); *hex++ = 48 + ((*bin)&15); bin ++; } } static void hex2bin(unsigned char *hex, unsigned char *bin, int n) { for (;n>0;n--) { *bin++ = ((hex[0] & 15) << 4) | (hex[1] & 15); hex += 2; } } #ifndef NO_NETWORK static void trace_print(const char *tag, const char *tag2, void *bvp, int nb) { unsigned char *bp; fprintf(stderr,"%s%s[%d] ",tag,tag2,nb); for (bp=bvp;nb>0;nb--,bp++) { switch (*bp) { case '\a': fprintf(stderr,"\\a"); break; case '\b': fprintf(stderr,"\\b"); break; case '\33':fprintf(stderr,"\\e"); break; case '\f': fprintf(stderr,"\\f"); break; case '\n': fprintf(stderr,"\\n"); break; case '\r': fprintf(stderr,"\\r"); break; case '\t': fprintf(stderr,"\\t"); break; case '\v': fprintf(stderr,"\\v"); break; default: if ((*bp < 32) || ((*bp > 126) && (*bp <= 160))) { if (bp[1] && (bp[1] >= '0') && (bp[1] <= '9')) { fprintf(stderr,"\\%03o",*bp); } else { fprintf(stderr,"\\%o",*bp); } } else { putc(*bp,stderr); } break; } } putc('\n',stderr); } #endif #ifndef NO_NETWORK static void run_tracer(int ctl, int death) { typedef struct client CLIENT; struct client { CLIENT *link; int fd; char *lbuf; int lalloc; int lfill; int fdx; } ; CLIENT *clients; struct pollfd *fdv; int fdva; int fdvn; CLIENT *c; CLIENT **cp; int addfd(int fd) { struct pollfd *f; if (fdvn >= fdva) fdv = realloc(fdv,(fdva=fdvn+1)*sizeof(*fdv)); f = &fdv[fdvn]; f->fd = fd; f->events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; return(fdvn++); } void client_line(CLIENT *c, char *addbuf, int addlen) { struct iovec iov[3]; int iox; static char nl = '\n'; iox = 0; if (c->lfill > 0) { iov[iox].iov_base = c->lbuf; iov[iox].iov_len = c->lfill; iox ++; } if (addlen > 0) { iov[iox].iov_base = addbuf; iov[iox].iov_len = addlen; iox ++; } iov[iox].iov_base = &nl; iov[iox].iov_len = 1; iox ++; writev(2,&iov[0],iox); c->lfill = 0; } void client_read(CLIENT *c) { char rbuf[512]; int rn; char *nl; int rx; int l; rn = read(c->fd,&rbuf[0],sizeof(rbuf)); if (rn < 0) { fprintf(stderr,"%s: tracer client read: %s\n",__progname,strerror(errno)); exit(1); } if (rn == 0) { if (c->lfill) client_line(c,0,0); close(c->fd); c->fd = -1; return; } rx = 0; while ((nl = memchr(&rbuf[rx],'\n',rn-rx))) { client_line(c,&rbuf[rx],nl-&rbuf[rx]); c->lfill = 0; rx = (nl+1) - &rbuf[0]; } l = rn - rx; if (l > 0) { if (c->lfill+l > c->lalloc) c->lbuf = realloc(c->lbuf,c->lalloc=c->lfill+l); bcopy(&rbuf[rx],c->lbuf+c->lfill,l); c->lfill += l; } } void ctl_read(void) { struct msghdr mh; struct iovec iov; #ifdef SCM_RIGHTS struct cmsghdr cmh; char cbuf[sizeof(struct cmsghdr)+sizeof(int)]; #endif int fd; char junk; CLIENT *c; mh.msg_name = 0; mh.msg_namelen = 0; iov.iov_base = &junk; iov.iov_len = 1; mh.msg_iov = &iov; mh.msg_iovlen = 1; #ifdef SCM_RIGHTS mh.msg_control = &cbuf[0]; mh.msg_controllen = sizeof(cbuf); mh.msg_flags = 0; #else mh.msg_accrights = (void *)&fd; mh.msg_accrightslen = sizeof(int); #endif if (recvmsg(ctl,&mh,0) < 0) { fprintf(stderr,"%s: can't receive trace fd: %s\n",__progname,strerror(errno)); exit(1); } #ifdef MSG_CTRUNC if (mh.msg_flags & MSG_CTRUNC) { fprintf(stderr,"%s: can't receive trace fd (control info truncated)\n",__progname); exit(1); } #endif #ifdef SCM_RIGHTS if (mh.msg_controllen < sizeof(cbuf)) { fprintf(stderr,"%s: can't receive trace fd (control info unexpectedly short)\n",__progname); exit(1); } bcopy(&cbuf[0],&cmh,sizeof(cmh)); if (cmh.cmsg_len != sizeof(cbuf)) { fprintf(stderr,"%s: can't receive trace fd (control packet len wrong)\n",__progname); exit(1); } if (cmh.cmsg_level != SOL_SOCKET) { fprintf(stderr,"%s: can't receive trace fd (control info level wrong)\n",__progname); exit(1); } if (cmh.cmsg_type != SCM_RIGHTS) { fprintf(stderr,"%s: can't receive trace fd (control info type wrong)\n",__progname); exit(1); } bcopy(&cbuf[sizeof(cmh)],&fd,sizeof(int)); #else if (mh.msg_accrightslen != sizeof(int)) { fprintf(stderr,"%s: can't receive trace fd (access rights unexpectedly short)\n",__progname); exit(1); } #endif c = malloc(sizeof(CLIENT)); c->fd = fd; c->lbuf = 0; c->lalloc = 0; c->lfill = 0; c->link = clients; clients = c; } clients = 0; fdv = 0; fdva = 0; while (1) { fdvn = 0; addfd(ctl); if (death >= 0) addfd(death); cp = &clients; while ((c = *cp)) { if (c->fd >= 0) { cp = &c->link; c->fdx = addfd(c->fd); } else { *cp = c->link; free(c->lbuf); free(c); } } if ((death < 0) && !clients) exit(0); if (poll(fdv,fdvn,INFTIM) < 0) { if (errno == EINTR) continue; fprintf(stderr,"%s; poll: %s\n",__progname,strerror(errno)); exit(1); } if (fdv[0].revents & (POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI)) { ctl_read(); continue; } if ((death >= 0) && (fdv[1].revents & (POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI))) { close(death); death = -1; continue; } for (c=clients;c;c=c->link) { if (fdv[c->fdx].revents & (POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI)) { client_read(c); } } } } #endif static int devnull(void) { static int fd = -1; if (fd < 0) fd = open("/dev/null",O_RDWR,0); return(fd); } static int mypipe(int *x) { char buf[32]; int rv; rv = pipe(x); if (rv < 0) return(-1); sprintf(&buf[0],"%d %d",x[0],x[1]); write(devnull(),&buf[0],strlen(&buf[0])); return(rv); } #define pipe(x) mypipe(x) #ifndef NO_NETWORK static int mysocketpair(int f, int d, int p, int *x) { char buf[32]; int rv; rv = socketpair(f,d,p,x); if (rv < 0) return(-1); sprintf(&buf[0],"%d %d",x[0],x[1]); write(devnull(),&buf[0],strlen(&buf[0])); return(rv); } #define socketpair(f,d,p,x) mysocketpair(f,d,p,x) #endif #ifndef NO_NETWORK static void fork_tracer(void) { int s[2]; int p[2]; pid_t kid; if (socketpair(AF_LOCAL,SOCK_DGRAM,0,&s[0]) < 0) { fprintf(stderr,"%s: socketpair AF_LOCAL SOCK_DGRAM: %s\n",__progname,strerror(errno)); exit(1); } if (pipe(&p[0]) < 0) { fprintf(stderr,"%s: pipe: %s\n",__progname,strerror(errno)); exit(1); } kid = fork(); if (kid < 0) { fprintf(stderr,"%s: fork: %s\n",__progname,strerror(errno)); exit(1); } if (kid == 0) { close(p[1]); /* don't close s[0]; let trace filter merger process hold it open to prevent ECONNRESET recv errors (we'd rather get death notification via the death pipe, here called p). */ run_tracer(s[1],p[0]); exit(0); } close(p[0]); /* drop p[1], let it be closed when we exit - but make sure any future children forked to run other programs don't inherit it! */ fcntl(p[1],F_SETFD,1); close(s[1]); trace_ctl = s[0]; } #endif #ifndef NO_NETWORK static void send_tracer(int fd) { struct msghdr mh; struct iovec iov; #ifdef SCM_RIGHTS struct cmsghdr cmh; char cbuf[sizeof(struct cmsghdr)+sizeof(int)]; #endif mh.msg_name = 0; mh.msg_namelen = 0; iov.iov_base = (void *)&iov; /* XXX, cast is for SunOS */ iov.iov_len = 1; mh.msg_iov = &iov; mh.msg_iovlen = 1; #ifdef SCM_RIGHTS cmh.cmsg_len = sizeof(cbuf); cmh.cmsg_level = SOL_SOCKET; cmh.cmsg_type = SCM_RIGHTS; bcopy(&cmh,&cbuf[0],sizeof(cmh)); bcopy(&fd,&cbuf[sizeof(cmh)],sizeof(fd)); mh.msg_control = &cbuf[0]; mh.msg_controllen = sizeof(cbuf); mh.msg_flags = 0; #else mh.msg_accrights = (void *)&fd; mh.msg_accrightslen = sizeof(int); #endif if (sendmsg(trace_ctl,&mh,0) < 0) { fprintf(stderr,"%s: can't send trace fd to tracer: %s\n",__progname,strerror(errno)); exit(1); } close(fd); } #endif #ifndef NO_NETWORK static void tracefilter(int rfd, int wfd, const char *tag) { int i; int p1[2]; int p2[2]; int p3[2]; unsigned char buf[WORKSIZE]; int flags; #define LIVE_R 1 #define LIVE_W 2 if (trace_ctl < 0) fork_tracer(); if ((pipe(&p1[0]) < 0) || (pipe(&p2[0]) < 0) || (pipe(&p3[0]) < 0)) { fprintf(stderr,"%s: pipe: %s\n",__progname,strerror(errno)); exit(1); } switch (fork()) { case -1: fprintf(stderr,"%s: can't fork: %s\n",__progname,strerror(errno)); exit(1); break; case 0: break; default: dup2(p1[0],rfd); dup2(p2[1],wfd); close(p1[0]); close(p1[1]); close(p2[0]); close(p2[1]); send_tracer(p3[0]); close(p3[1]); return; break; } if (p3[1] != 2) { dup2(p3[1],2); close(p3[1]); } for (i=getdtablesize()-1;i>=0;i--) { if ((i != 2) && (i != p1[1]) && (i != p2[0]) && (i != rfd) && (i != wfd)) { close(i); } } flags = LIVE_R | LIVE_W; while (flags & (LIVE_R|LIVE_W)) { fd_set fds; int n; FD_ZERO(&fds); if (flags & LIVE_W) FD_SET(p2[0],&fds); if (flags & LIVE_R) FD_SET(rfd,&fds); n = select(FD_SETSIZE,&fds,(fd_set *)0,(fd_set *)0,(struct timeval *)0); if (n < 0) { if (errno != EINTR) { fprintf(stderr,"%s: select error: %s\n",__progname,strerror(errno)); exit(1); } continue; } if ((flags & LIVE_W) && FD_ISSET(p2[0],&fds)) { n = read(p2[0],&buf[0],sizeof(buf)); if (n < 0) { fprintf(stderr,"%s: read error: %s\n",__progname,strerror(errno)); exit(1); } if (n == 0) { fprintf(stderr,"%s [closed >>]\n",tag); close(p2[0]); close(wfd); flags &= ~LIVE_W; } else { trace_print(tag,">>",&buf[0],n); write(wfd,&buf[0],n); } } if ((flags & LIVE_R) && FD_ISSET(rfd,&fds)) { n = read(rfd,&buf[0],sizeof(buf)); if (n < 0) { fprintf(stderr,"%s: read error: %s\n",__progname,strerror(errno)); exit(1); } if (n == 0) { fprintf(stderr,"%s [closed <<]\n",tag); close(rfd); close(p1[1]); flags &= ~LIVE_R; } else { trace_print(tag,"<<",&buf[0],n); write(p1[1],&buf[0],n); } } } exit(0); #undef LIVE_R #undef LIVE_W } #endif static void xfilter(int rfd, int wfd, const char *tag) { int i; int p1[2]; int p2[2]; unsigned char binbuf[256]; unsigned char hexbuf[512]; unsigned char oddsave; int flags; #define SHUT_R 1 #define SHUT_W 2 #define ODDBYTE 4 #ifdef NO_NETWORK tag = tag; #endif if ((pipe(&p1[0]) < 0) || (pipe(&p2[0]) < 0)) { fprintf(stderr,"%s: can't create pipe: %s\n",__progname,strerror(errno)); exit(1); } switch (fork()) { case -1: fprintf(stderr,"%s: can't fork: %s\n",__progname,strerror(errno)); exit(1); break; case 0: break; default: dup2(p1[0],rfd); close(p1[1]); close(p2[0]); dup2(p2[1],wfd); return; break; } for (i=getdtablesize()-1;i>=0;i--) { if ((i != 2) && (i != p1[1]) && (i != p2[0]) && (i != rfd) && (i != wfd)) { close(i); } } flags = 0; while (1) { fd_set fds; int n; if ((flags & (SHUT_R|SHUT_W)) == (SHUT_R|SHUT_W)) exit(0); FD_ZERO(&fds); if (! (flags & SHUT_W)) FD_SET(p2[0],&fds); if (! (flags & SHUT_R)) FD_SET(rfd,&fds); n = select(FD_SETSIZE,&fds,(fd_set *)0,(fd_set *)0,(struct timeval *)0); if (n < 0) { if (errno != EINTR) { fprintf(stderr,"%s: select error: %s\n",__progname,strerror(errno)); exit(1); } continue; } if (FD_ISSET(p2[0],&fds)) { n = read(p2[0],(char *)&binbuf[0],sizeof(binbuf)); if (n < 0) { fprintf(stderr,"%s: bin read error: %s\n",__progname,strerror(errno)); exit(1); } if (n == 0) { close(wfd); flags |= SHUT_W; } else { bin2hex(&binbuf[0],&hexbuf[0],n); #ifndef NO_NETWORK if (flag_trace) trace_print(tag,"(enc)>>",&hexbuf[0],n*2); #endif write(wfd,(char *)&hexbuf[0],n*2); } } if (FD_ISSET(rfd,&fds)) { if (flags & ODDBYTE) { n = read(rfd,(char *)&hexbuf[1],sizeof(hexbuf)-1); if (n < 0) { fprintf(stderr,"%s: hex read error: %s\n",__progname,strerror(errno)); exit(1); } if (n == 0) { close(p1[1]); flags |= SHUT_R; } else { n ++; hexbuf[0] = oddsave; flags &= ~ODDBYTE; } } else { n = read(rfd,(char *)&hexbuf[0],sizeof(hexbuf)); if (n < 0) { fprintf(stderr,"%s: hex read error: %s\n",__progname,strerror(errno)); exit(1); } if (n == 0) { close(p1[1]); flags |= SHUT_R; } } if (n) { if (n & 1) { oddsave = hexbuf[--n]; flags |= ODDBYTE; } n /= 2; #ifndef NO_NETWORK if (flag_trace) trace_print(tag,"(enc)<<",&hexbuf[0],n*2); #endif hex2bin(&hexbuf[0],&binbuf[0],n); write(p1[1],(char *)&binbuf[0],n); } } } #undef SHUT_R #undef SHUT_W #undef ODDBYTE } static void newremote(char *arg) { REMOTE *r; char *slash; char *at; char *colon; char *bang; char *bracket; char *ebracket; auto void badspec(const char *) __attribute__((__noreturn__)); void badspec(const char *why) { fprintf(stderr,"%s: %s: %s\n",__progname,arg,why); exit(1); } r = NEW(REMOTE); r->rfd = -1; r->wfd = -1; r->link = remotes; remotes = r; r->fullarg = arg; arg = strdup(arg); if (arg[0] == ':') { r->machine = (void *) r; r->user = arg; } else do { slash = index(arg,'/'); at = index(arg,'@'); colon = index(arg,':'); bang = index(arg,'!'); bracket = index(arg,'['); ebracket = bracket ? index(bracket+1,']') : 0; /* can have user machine program directory @ no yes iff user given yes yes ! yes no yes yes : no yes iff in [] no only after a / / no no yes yes [ no yes (but bad idea) yes yes ] yes yes iff not in [] yes yes user @ machine ! program : directory (case 1) user @ [ machine ] ! program : directory (case 2) machine ! program : directory (case 3) [ machine ] ! program : directory (case 4) user @ machine : directory (case 5) user @ [ machine ] : directory (case 6) machine : directory (case 7) [ machine ] : directory (case 8) directory (case 9) */ if ( at && bang && colon && (at < bang) && (bang < colon) && (!slash || (bang < slash)) && (!bracket || (bang < bracket)) ) { /* case 1 */ r->user = arg; *at = '\0'; r->machine = at + 1; *bang = 0; r->program = bang + 1; *colon = 0; r->directory = colon + 1; break; } if ( at && colon && (at < colon) && (!bang || (colon < bang)) && (!slash || (colon < slash)) && (!bracket || (colon < bracket)) ) { /* case 5 */ r->user = arg; *at = '\0'; r->machine = at + 1; *bang = 0; r->program = bang + 1; *colon = 0; r->directory = colon + 1; break; } if (at && bracket && (bracket == at+1) && (!slash || (at < slash))) { /* case 2 or 6, or error */ if (! ebracket) badspec("unclosed ["); bang = index(ebracket+1,'!'); colon = index(ebracket+1,':'); slash = index(ebracket+1,'/'); if ((bang == ebracket+1) && colon) { /* case 2 */ r->user = arg; *at = '\0'; r->machine = bracket + 1; *ebracket = '\0'; r->program = bang + 1; *colon = '\0'; r->directory = colon + 1; break; } if (colon == ebracket+1) { /* case 6 */ r->user = arg; *at = '\0'; r->machine = bracket + 1; *ebracket = '\0'; r->program = 0; r->directory = colon + 1; break; } badspec("[ ] machine name not followed by !...: or :"); } if (bracket == arg) { /* case 4 or 8, or error */ if (! ebracket) badspec("unclosed ["); bang = index(ebracket+1,'!'); colon = index(ebracket+1,':'); slash = index(ebracket+1,'/'); if ((bang == ebracket+1) && colon) { /* case 4 */ r->user = 0; r->machine = bracket + 1; *ebracket = '\0'; r->program = bang + 1; *colon = '\0'; r->directory = colon + 1; break; } if (colon == ebracket+1) { /* case 8 */ r->user = 0; r->machine = bracket + 1; *ebracket = '\0'; r->program = 0; r->directory = colon + 1; break; } badspec("[ ] machine name not followed by !...: or :"); } if (bang && colon && (bang < colon) && (!slash || (bang < slash))) { /* case 3 */ r->user = 0; r->machine = arg; *bang = '\0'; r->program = bang + 1; *colon = '\0'; r->directory = colon + 1; break; } if (colon && (!slash || (colon < slash))) { /* case 7 */ r->user = 0; r->machine = arg; *colon = '\0'; r->program = 0; r->directory = colon + 1; break; } /* case 9 */ r->user = 0; r->machine = 0; r->program = 0; r->directory = arg; } while (0); r->rsh = currsh; r->ignnx = flag_nextinx; r->mappings = pendmap; pendmap = 0; r->links = 0; flag_nextinx = 0; } static void reaper(int sig __attribute__((__unused__))) { while (wait3(0,WNOHANG,0) > 0) ; } static void sgetnts(void) { int x; int c; x = 0; while (1) { c = getchar(); if (c == EOF) { fprintf(stderr,"%s: unexpected EOF (in sgetnts)\n",__progname); exit(1); } slavework[x] = c; if (c == '\0') return; if (x < WORKSIZE-1) x ++; } } static void etc_init(ENTRY_TCONC *etc) { etc->tail = &etc->list; } static void etc_add(ENTRY_TCONC *etc, ENTRY *e) { *etc->tail = e; etc->tail = &e->link; } static void etc_end(ENTRY_TCONC *etc) { *etc->tail = 0; } static void etc_append(ENTRY_TCONC *etc, ENTRY *e) { if (! e) return; *etc->tail = e; while (e->link) e = e->link; etc->tail = &e->link; } static void fork_filter(int ifd, int ofd, const char *argv0, ...) { va_list ap; int p[2]; int na; const char **av; void (*fn)(void); int kid; int i; if ((ifd < 0) && (ofd < 0)) panic("fork_filter: both fds negative"); if ((ifd >= 0) && (ofd >= 0)) panic("fork_filter: neither fd negative"); if (pipe(p) < 0) panic("fork_filter: can't make pipe: %s",strerror(errno)); if (argv0) { va_start(ap,argv0); for (na=1;va_arg(ap,const char *);na++) ; va_end(ap); av = malloc((na+1)*sizeof(const char *)); av[0] = argv0; va_start(ap,argv0); for (na=1;(av[na]=va_arg(ap,const char *));na++) ; va_end(ap); } else { typedef void (*foo)(void); /* stupid varargs misfeature */ va_start(ap,argv0); fn = va_arg(ap,foo); va_end(ap); } while (wait3(0,WNOHANG,0) > 0) ; kid = fork(); if (kid) { if (ifd < 0) { close(ofd); close(p[0]); dup2(p[1],ofd); close(p[1]); } else { close(ifd); close(p[1]); dup2(p[0],ifd); close(p[0]); } return; } for (i=getdtablesize()-1;i>2;i--) { if ((i != ifd) && (i != ofd) && (i != p[0]) && (i != p[1])) close(i); } if (ifd < 0) { if (ofd != 1) { dup2(ofd,1); close(ofd); } if (p[0] != 0) { dup2(p[0],0); close(p[0]); } close(p[1]); } else { if (ifd != 0) { dup2(ifd,0); close(ifd); } if (p[1] != 1) { dup2(p[1],1); close(p[1]); } close(p[0]); } if (argv0) { execvp(argv0,(const void *)av); perror(argv0); } else { (*fn)(); } exit(1); } static void slave_rm(void); /* forward */ /* always sets errno */ static void slave_rm_r(void) { ENTRY_TCONC etc; ENTRY *e; DIR *dir; struct direct *d; int err; int len; err = 0; etc_init(&etc); dir = opendir(&slavetemp[0]); while ((d=readdir(dir))) { if ( (d->d_name[0] == '.') && ( DLEN_IS(d,1) || ( (d->d_name[1] == '.') && DLEN_IS(d,2) ) ) ) continue; e = NEW(ENTRY); e->name = strdup(&d->d_name[0]); etc_add(&etc,e); } closedir(dir); etc_end(&etc); len = strlen(&slavetemp[0]); slavetemp[len++] = '/'; while (etc.list) { e = etc.list; etc.list = e->link; strcpy(&slavetemp[len],e->name); slave_rm(); if (err == 0) err = errno; free(e->name); OLD(e); } slavetemp[len-1] = '\0'; errno = err; } /* unlink(2) on a directory either produces EPERM or orphans the stuff in the directory, depending on system; it doesn't necessarily do anything sensible and distinctive, like EISDIR, so we have to waste a syscall.... */ /* always sets errno */ static void slave_rm(void) { errno = 0; if (rmdir(&slavetemp[0]) == 0) return; switch (errno) { case ENOTEMPTY: slave_rm_r(); if ((errno == 0) && (rmdir(&slavetemp[0]) == 0)) errno = 0; break; case ENOTDIR: if (unlink(&slavetemp[0]) == 0) errno = 0; break; default: break; } } static int makenulls(int fd, unsigned long int bytes) { int n; int r; int w; char rbuf[512]; while (bytes > 0) { n = (bytes < 512) ? bytes : 512; r = read(fd,&rbuf[0],n); if (r < 0) return(-1); if (r == 0) return((lseek(fd,bytes,L_INCR)<0)?-1:0); if (bcmp(&rbuf[0],&zeroblk[0],r)) { if (lseek(fd,-r,L_INCR) < 0) return(-1); w = write(fd,&zeroblk[0],r); if (w < 0) return(-1); if (w < r) return(EIO); } bytes -= r; } return(0); } static void swrite(int skip, const void *buf, int nb) { int w; if ((skip > 0) && (makenulls(1,skip*512) < 0)) { fprintf(stderr,"%s: sparsewrite seek error: %s\n",__progname,strerror(errno)); exit(1); } if (nb > 0) { w = write(1,buf,nb); if (w != nb) { if (w < 0) { fprintf(stderr,"%s: sparsewrite write error: %s\n",__progname,strerror(errno)); exit(1); } fprintf(stderr,"%s: sparsewrite write: tried %d, got %d\n",__progname,nb,w); exit(1); } } } /* not all ftruncate()s will grow short files, grr! */ static void setsize(int fd, unsigned long long int size) { struct stat stb; ftruncate(fd,size); fstat(fd,&stb); if (stb.st_size < size) { lseek(fd,size+(1ULL<<20),L_SET); write(fd,"",1); ftruncate(fd,size); } } static void sparsewrite(void) { int inbuf; int base; int n; int skip; int w0; unsigned long long int size; inbuf = 0; skip = 0; size = 0; while (1) { if (inbuf == 0) base = 0; n = read(0,&slavework[base],WORKSIZE-base); if (n < 0) { fprintf(stderr,"%s: sparsewrite read error: %s\n",__progname,strerror(errno)); exit(1); } if (n == 0) { swrite(skip,&slavework[base],inbuf); setsize(1,size); exit(0); } size += n; inbuf += n; w0 = -1; while (inbuf >= 512) { if (bcmp(&slavework[base],&zeroblk[0],512)) { if (w0 < 0) w0 = base; } else { if (w0 >= 0) { swrite(skip,&slavework[w0],base-w0); skip = 0; w0 = -1; } skip ++; } inbuf -= 512; base += 512; } if (w0 >= 0) { swrite(skip,&slavework[w0],base-w0); skip = 0; } } } static void slavepathcheck(void) { char *dp; char *wp; wp = &slavework[0]; if ((wp[0] == '.') && (wp[1] == '.') && ((wp[2] == '/') || (wp[2] == '\0'))) { badpath:; fprintf(stderr,"%s: bad path %s from master\n",__progname,&slavework[0]); exit(1); } wp ++; while (1) { dp = index(wp,'.'); if (! dp) break; if ( (dp[-1] == '/') && (dp[1] == '.') && ((dp[2] == '\0') || (dp[2] == '/')) ) goto badpath; wp = dp + 1; } } static void slave_D(void) { DIR *dir; struct direct *d; int l; int ll; struct stat stb; sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); dir = opendir(&slavetemp[0]); if (dir == 0) { putchar('\0'); return; } l = strlen(slavetemp); slavetemp[l++] = '/'; while ((d=readdir(dir))) { if ( (d->d_ino == 0) || ( (d->d_name[0] == '.') && ( (d->d_name[1] == '\0') || ( (d->d_name[1] == '.') && (d->d_name[2] == '\0') ) ) ) ) continue; strcpy(&slavetemp[l],d->d_name); if ((flag_follow?stat(&slavetemp[0],&stb):lstat(&slavetemp[0],&stb)) < 0) continue; fputs(d->d_name,stdout); putchar('\0'); printf("%d %d %d",(int)stb.st_mode,(int)stb.st_uid,(int)stb.st_gid); if (flag_mtimes) printf(" %ld",(long int)stb.st_mtime); if (flag_links) printf(" %d %llu %llu", (int)stb.st_nlink, (unsigned long long int)stb.st_dev, (unsigned long long int)stb.st_ino ); switch (stb.st_mode & S_IFMT) { case S_IFDIR: break; case S_IFCHR: case S_IFBLK: printf(" %llu %llu",(unsigned long long int)major(stb.st_rdev),(unsigned long long int)minor(stb.st_rdev)); break; case S_IFREG: printf(" %llu",(unsigned long long int)stb.st_size); break; case S_IFLNK: putchar('\0'); ll = readlink(&slavetemp[0],&slavework[0],sizeof(slavework)); if (ll < 0) ll = 0; fwrite(&slavework[0],1,ll,stdout); break; case S_IFSOCK: break; #ifdef S_IFIFO case S_IFIFO: break; #endif } putchar('\0'); } closedir(dir); putchar('\0'); } static void slave_I(void) { int fd; int cmd; int nb; int nr; unsigned long long int off; unsigned long long int curoff; sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); fd = open(&slavetemp[0],O_RDONLY,0); if (flag_debug) fprintf(stderr,"slave_I entering command loop\n"); curoff = 0; while (1) { fflush(stdout); cmd = getchar(); switch (cmd) { default: fprintf(stderr,"%s: protocol error, unknown slave I subcommand %d (%c)\n",__progname,cmd,cmd); exit(1); break; case EOF: fprintf(stderr,"%s: protocol error, unexpected EOF (slave_I)\n",__progname); exit(1); break; case 'c': goto out; /* aka break 2 */ break; case 'r': sgetnts(); sscanf(&slavework[0],"%d%llu",&nb,&off); if (nb > WORKSIZE) { fprintf(stderr,"%s: protocol error, nb too big (%d > %d) in slave R r\n",__progname,nb,WORKSIZE); exit(1); } if (off != curoff) lseek(fd,off,L_SET); curoff = off; nr = read(fd,&slavetemp[0],nb); if (nr <= 0) { printf("%d",COPYBLK_HOLE); putchar('\0'); } else { if (nr < nb) bzero(&slavetemp[nr],nb-nr); if (bcmp(&slavetemp[0],&zeroblk[0],nb)) { printf("%d",COPYBLK_DATA); putchar('\0'); fwrite(&slavetemp[0],1,nb,stdout); } else { printf("%d",COPYBLK_HOLE); putchar('\0'); } } curoff += (nr >= 0) ? nr : 0; break; } } out:; if (fd >= 0) close(fd); } static void slave_K(void) { int fd; void *md5; int nb; unsigned long long int from; unsigned long long int to; int n; char cksum[16]; sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); sgetnts(); sscanf(&slavework[0],"%llu %llu",&from,&to); if (flag_debug) fprintf(stderr,"slave_K %s %llu %llu\n",&slavetemp[0],from,to); fd = open(&slavetemp[0],O_RDONLY,0); if (fd < 0) { printf("O%d",errno); putchar('\0'); } else { md5 = md5_init(); lseek(fd,from,SEEK_SET); to -= from; while (1) { n = ullmin(to,WORKSIZE); if (n < 1) { md5_result(md5,&cksum[0]); printf("K"); fwrite(&cksum[0],1,16,stdout); break; } nb = n ? read(fd,&slavetemp[0],n) : 0; if (nb < 0) { printf("R%d",errno); putchar('\0'); break; } if (nb == 0) { printf("R0"); putchar('\0'); break; } md5_process_bytes(md5,&slavetemp[0],nb); to -= nb; } close(fd); } } static void slave_L(void) { sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); sgetnts(); sprintf(&slavetemp2[0],"%s/%s",&slavebase[0],&slavework[0]); if (flag_debug) fprintf(stderr,"slave_L %s -> %s\n",&slavetemp[0],&slavetemp2[0]); if (flag_readonly) { printf("%d",EROFS); putchar('\0'); return; } if (link(&slavetemp2[0],&slavetemp[0]) == 0) errno = 0; printf("%d",errno); putchar('\0'); } static void slave_R(void) { int fd; int cmd; int nb; int nr; unsigned long long int off; unsigned long long int curoff; sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); fd = open(&slavetemp[0],O_RDONLY,0); if (flag_debug) fprintf(stderr,"slave_R entering command loop\n"); curoff = 0; while (1) { fflush(stdout); cmd = getchar(); switch (cmd) { default: fprintf(stderr,"%s: protocol error, unknown slave R subcommand %d (%c)\n",__progname,cmd,cmd); exit(1); break; case EOF: fprintf(stderr,"%s: protocol error, unexpected EOF (slave_R)\n",__progname); exit(1); break; case 'c': if (fd >= 0) close(fd); return; break; case 'r': sgetnts(); sscanf(&slavework[0],"%d%llu",&nb,&off); if (nb > WORKSIZE) { fprintf(stderr,"%s: protocol error, nb too big (%d > %d) in slave R r\n",__progname,nb,WORKSIZE); exit(1); } if (off != curoff) lseek(fd,curoff,L_SET); curoff = off; nr = read(fd,&slavetemp[0],nb); if (nr <= 0) { curoff = -1; fwrite(&zeroblk[0],1,nb,stdout); } else { curoff += nr; if (nr < nb) { fwrite(&slavetemp[0],1,nr,stdout); fwrite(&zeroblk[0],1,nb-nr,stdout); } else { fwrite(&slavetemp[0],1,nb,stdout); } } break; } } } static void slave_d(void) { int mode; int uid; int gid; struct stat stb; sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); sgetnts(); sscanf(&slavework[0],"%d%d%d",&mode,&uid,&gid); if (flag_debug) fprintf(stderr,"slave_d %s %o %d %d\n",&slavetemp[0],mode,uid,gid); if (flag_readonly) { printf("%d",EROFS); putchar('\0'); return; } if ( (mkdir(&slavetemp[0],mode) == 0) && (stat(&slavetemp[0],&stb) == 0) && ( (((stb.st_mode^mode) & (flag_sgiddir?05777:07777)) == 0) || (chmod(&slavetemp[0],flag_sgiddir?((stb.st_mode&02000)|(mode&05777)):mode) == 0) ) && ( flag_noown || ( (chown(&slavetemp[0],-1,gid) == 0) && (chown(&slavetemp[0],uid,-1) == 0) ) ) ) errno = 0; printf("%d",errno); putchar('\0'); } static void slave_f(void) { int mode; int uid; int gid; sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); sgetnts(); sscanf(&slavework[0],"%d%d%d",&mode,&uid,&gid); if (flag_debug) fprintf(stderr,"slave_f %s %o %d %d\n",&slavetemp[0],mode,uid,gid); if (flag_readonly) { printf("%d",EROFS); putchar('\0'); return; } #ifdef S_IFIFO if ( (mkfifo(&slavetemp[0],mode) == 0) && ( flag_noown || ( (chown(&slavetemp[0],-1,gid) == 0) && (chown(&slavetemp[0],uid,-1) == 0) ) ) ) errno = 0; #else errno = ENODEV; #endif printf("%d",errno); putchar('\0'); } static int open_to_update(const char *path, int mode, int *remodp) { int rechmod; int fd; rechmod = -1; mode &= 07777; fd = open(path,O_RDWR|O_CREAT,mode); if (fd < 0) { fd = open(path,O_WRONLY|O_CREAT,mode); if (fd < 0) { struct stat stb; if (errno != EACCES) return(-1); if ( (stat(path,&stb) < 0) || (chmod(path,0600) < 0) ) { errno = EACCES; return(-1); } rechmod = stb.st_mode & 07777; fd = open(path,O_RDWR,0); if (fd < 0) { int e; e = errno; chmod(path,rechmod); errno = e; return(-1); } } } *remodp = rechmod; return(fd); } static void slave_i(void) { int mode; int uid; int gid; unsigned long long int size; unsigned long long int from; unsigned long long int to; int fd; unsigned long long int left; int blkleft; int blkoff; int flg; int n; int r; int tempfill; int skip; int w0; int writearg; int writerv; int finalerr; int rechmod; sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); sgetnts(); sscanf(&slavework[0],"%d%d%d%llu%llu%llu",&mode,&uid,&gid,&size,&from,&to); if (flag_debug) fprintf(stderr,"slave_i %s %o %d %d %llu %llu %llu\n",&slavetemp[0],mode&07777,uid,gid,size,from,to); if (flag_readonly) { printf("%d",EROFS); putchar('\0'); return; } fd = open_to_update(&slavetemp[0],mode&07777,&rechmod); if (fd < 0) { printf("%d",errno); putchar('\0'); return; } if ( ( !flag_noown && ( (fchown(fd,-1,gid) < 0) || (fchown(fd,uid,-1) < 0) ) ) || (lseek(fd,from,SEEK_SET) < 0) ) { printf("%d",errno); putchar('\0'); if (rechmod >= 0) fchmod(fd,rechmod); close(fd); return; } putchar('0'); putchar('\0'); fflush(stdout); blkleft = 0; blkoff = 0; tempfill = 0; left = to - from; skip = 0; finalerr = 0; #define WRITE(buf,nb) do { if ((writearg=(nb)) && (writerv=write(fd,(buf),writearg)) != writearg) goto writeerr; } while (0) while ((left > 0) || (blkleft > 0)) { if (0) { writeerr:; finalerr = (writerv < 0) ? errno : EIO; if (rechmod >= 0) fchmod(fd,rechmod); close(fd); fd = -1; } if (0) { fderr:; finalerr = errno; if (rechmod >= 0) fchmod(fd,rechmod); close(fd); fd = -1; } if (blkleft < 1) { sgetnts(); if (sscanf(&slavework[0],"%d%d",&n,&flg) != 2) { fprintf(stderr,"%s: protocol error (can't read block size and type)\n",__progname); exit(1); } blkleft += n; blkoff = 0; left -= n; } switch (flg) { case COPYBLK_DATA: n = WORKSIZE - tempfill; if (blkleft < n) n = blkleft; r = fread(&slavework[tempfill],1,n,stdin); if (r != n) { fprintf(stderr,"%s: protocol error (can't read block data)\n",__progname); exit(1); } blkleft -= n; blkoff += n; if (fd < 0) continue; n += tempfill; r = 0; w0 = -1; while (n-r >= 512) { if (flag_nosparse || bcmp(&slavework[r],&zeroblk[0],512)) { if (w0 < 0) w0 = r; if (skip > 0) { if (makenulls(fd,skip) < 0) goto fderr; skip = 0; } } else { if (w0 >= 0) { WRITE(&slavework[w0],r-w0); w0 = -1; } skip += 512; } r += 512; } if (w0 >= 0) WRITE(&slavework[w0],r-w0); tempfill = n - r; if (tempfill && (r > 0)) bcopy(&slavework[r],&slavework[0],tempfill); break; case COPYBLK_HOLE: n = blkleft; blkleft = 0; blkoff += n; if (fd < 0) continue; if (tempfill+n < 512) { bzero(&slavework[tempfill],n); tempfill += n; } else { if (flag_nosparse) { if (tempfill > 0) { bzero(&slavework[tempfill],512-tempfill); WRITE(&slavework[0],512); n -= 512 - tempfill; } while (n >= WORKSIZE) { WRITE(&zeroblk[0],WORKSIZE); n -= WORKSIZE; } if (n >= 512) { WRITE(&zeroblk[0],n&~511); n &= 511; } if (n > 0) bzero(&slavework[0],n); tempfill = n; } else { if (tempfill) { if (bcmp(&slavework[0],&zeroblk[0],tempfill)) { bzero(&slavework[tempfill],512-tempfill); if (skip > 0) { if (makenulls(fd,skip) < 0) goto fderr; skip = 0; } WRITE(&slavework[0],512); n -= 512 - tempfill; } else { n += tempfill; } } skip += n; tempfill = skip % 512; if (tempfill) { skip -= tempfill; bzero(&slavework[0],tempfill); } } } break; } } if (skip > 0) { if (makenulls(fd,skip) < 0) { finalerr = errno; if (rechmod >= 0) fchmod(fd,rechmod); close(fd); fd = -1; } skip = 0; } if ((tempfill > 0) && (fd >= 0)) WRITE(&slavework[0],tempfill); if (fd >= 0) { setsize(fd,size); if (rechmod >= 0) fchmod(fd,rechmod); close(fd); putchar('0'); } else { printf("%d",finalerr); } #undef WRITE putchar('\0'); } static void slave_l(void) { int mode; int uid; int gid; sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); sgetnts(); sscanf(&slavework[0],"%d%d%d",&mode,&uid,&gid); sgetnts(); if (flag_debug) fprintf(stderr,"slave_l %s -> %s\n",&slavetemp[0],&slavework[0]); if (flag_readonly) { printf("%d",EROFS); putchar('\0'); return; } if ( (symlink(&slavework[0],&slavetemp[0]) == 0) && (lchmod(&slavetemp[0],mode) == 0) && ( flag_noown || ( (lchown(&slavetemp[0],-1,gid) == 0) && (lchown(&slavetemp[0],uid,-1) == 0) ) ) ) errno = 0; printf("%d",errno); putchar('\0'); } static void slave_m(void) { int mode; sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); sgetnts(); sscanf(&slavework[0],"%d",&mode); if (flag_debug) fprintf(stderr,"slave_m %s %o\n",&slavetemp[0],mode&07777); if (flag_readonly) { printf("%d",EROFS); putchar('\0'); return; } if (flag_sgiddir) { struct stat stb; if ((stat(&slavetemp[0],&stb) == 0) && ((stb.st_mode & S_IFMT) == S_IFDIR)) { mode = (mode & 05777) | (stb.st_mode & 02000); } if (lchmod(&slavetemp[0],mode) == 0) errno = 0; } else { if (lchmod(&slavetemp[0],mode) == 0) errno = 0; } printf("%d",errno); putchar('\0'); } static void slave_n(void) { int mode; int uid; int gid; unsigned long long int rdev_maj; unsigned long long int rdev_min; sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); sgetnts(); sscanf(&slavework[0],"%d %d %d %llu %llu",&mode,&uid,&gid,&rdev_maj,&rdev_min); if (flag_debug) fprintf(stderr,"slave_n %s %o %d %d (%llu,%llu)\n",&slavetemp[0],mode,uid,gid,rdev_maj,rdev_min); if (flag_readonly) { printf("%d",EROFS); putchar('\0'); return; } if ( (mknod(&slavetemp[0],mode,makedev(rdev_maj,rdev_min)) == 0) && ( flag_noown || ( (chown(&slavetemp[0],-1,gid) == 0) && (chown(&slavetemp[0],uid,-1) == 0) ) ) ) errno = 0; printf("%d",errno); putchar('\0'); } static void slave_o(void) { int uid; int gid; sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); sgetnts(); sscanf(&slavework[0],"%d %d",&uid,&gid); if (flag_readonly) { printf("%d",EROFS); putchar('\0'); return; } if (flag_debug) fprintf(stderr,"slave_o %s %d %d\n",&slavetemp[0],uid,gid); if ( flag_noown || ( (lchown(&slavetemp[0],-1,gid) == 0) && (lchown(&slavetemp[0],uid,-1) == 0) ) ) errno = 0; printf("%d",errno); putchar('\0'); } static void slave_r(void) { sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); if (flag_debug) fprintf(stderr,"slave_r %s\n",&slavetemp[0]); if (flag_readonly) { printf("%d",EROFS); putchar('\0'); return; } slave_rm(); printf("%d",errno); putchar('\0'); } static void slave_t(void) { unsigned long long int sz; sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); sgetnts(); sscanf(&slavework[0],"%llu",&sz); if (flag_debug) fprintf(stderr,"slave_t %s %llu\n",&slavetemp[0],sz); if (flag_readonly) { printf("%d",EROFS); putchar('\0'); return; } /* not all truncate()s will grow short files, grr! */ if (truncate(&slavetemp[0],sz) >= 0) { struct stat stb; if (stat(&slavetemp[0],&stb) >= 0) { if (stb.st_size < sz) { int fd; fd = open(&slavetemp[0],O_RDWR,0); if (fd >= 0) { lseek(fd,sz+(1ULL<<20),L_SET); write(fd,"",1); ftruncate(fd,sz); close(fd); errno = 0; } } else if (stb.st_size > sz) { errno = ERANGE; } else { errno = 0; } } } printf("%d",errno); putchar('\0'); } static void slave_u(void) { struct timeval times[2]; struct stat stb; unsigned long int sec; unsigned long int usec; sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); sgetnts(); sscanf(&slavework[0],"%lu %lu",&sec,&usec); times[1].tv_sec = sec; times[1].tv_usec = usec; stat(&slavetemp[0],&stb); times[0].tv_sec = stb.st_atime; times[0].tv_usec = 0; if (flag_debug) { time_t tmp; tmp = stb.st_atime; fprintf(stderr,"slave_u %s %.24s\n",&slavetemp[0],ctime(&tmp)); } if (flag_readonly) { printf("%d",EROFS); putchar('\0'); return; } if (lutimes(&slavetemp[0],×[0]) == 0) errno = 0; printf("%d",errno); putchar('\0'); } static int copy_within(int fd, unsigned long long int from, unsigned long long int to, unsigned long long int len) { char dbuf[65536]; int n; int r; int w; while (len > 0) { n = sizeof(dbuf); if (len < n) n = len; r = pread(fd,&dbuf[0],n,from); if (r < 0) return(errno); if (r != n) return(EIO); w = pwrite(fd,&dbuf[0],n,to); if (w != n) return(EIO); from += n; to += n; len -= n; } return(0); } static void ignore_stdin(unsigned long long int len) { for (;len>0;len--) getchar(); } static int copy_stdin(int fd, unsigned long long int to, int len) { char dbuf[65536]; int n; int r; int w; while (len > 0) { n = sizeof(dbuf); if (len < n) n = len; r = fread(&dbuf[0],1,n,stdin); if (r != n) return(EIO); w = pwrite(fd,&dbuf[0],n,to); if (w != n) return(EIO); to += n; len -= n; } return(0); } static void slave_y(void) { typedef struct htent HTENT; typedef struct sums SUMS; struct sums { unsigned int blk; unsigned int crc; unsigned char md5[16]; } ; struct htent { long long int n; long long int base; } ; int i; int j; int n; int fd; int rechmod; int mode; int uid; int gid; int err; unsigned long long int size; unsigned long long int blks; unsigned long long int at; unsigned long long int at0; unsigned long long int loc; unsigned long long int len; struct stat stb; static unsigned int crctable1[256] = { [128] = 0 }; static unsigned int crctable2[256]; unsigned int crc; void *md5; static unsigned char buf[65536]; int ifill; int iptr; static unsigned char sumbuf[RSYNC_BLKSIZE]; int sumdrop; int sumptr; int sumfill; static HTENT ht[RSYNC_HTSIZE]; HTENT *e; SUMS *sumv; static unsigned char genbuf[8192]; int genlen; unsigned int genblk; unsigned int genblk0; #define L(x) (((x)<<1)+1) #define R(x) (((x)+1)<<1) #define U(x) (((x)-1)>>1) void gen_flush(void) { if (flag_debug) fprintf(stderr,"gen flush\n"); if (genlen > 0) { if (flag_debug) fprintf(stderr,"gen flush: data %d\n",genlen); printf("d%d",genlen); putchar('\0'); fwrite(&genbuf[0],1,genlen,stdout); } else if (genlen < 0) { if (flag_debug) fprintf(stderr,"gen flush: blocks %u..%u\n",genblk0,genblk); printf("b%u %d",genblk0,genblk+1-genblk0); putchar('\0'); } genlen = 0; } void gen_block(unsigned int blk) { if (flag_debug) fprintf(stderr,"gen block %u\n",blk); if ((genlen > 0) || ((genlen < 0) && (genblk != blk-1))) gen_flush(); if (genlen == 0) { genblk0 = blk; genlen = -1; } genblk = blk; } void gen_byte(unsigned char b) { if (flag_debug) fprintf(stderr,"gen byte %02x\n",b); if ((genlen < 0) || (genlen >= sizeof(genbuf))) gen_flush(); genbuf[genlen++] = b; } int sumcmp(const SUMS *a, const SUMS *b) { unsigned int ah; unsigned int bh; ah = a->crc % RSYNC_HTSIZE; bh = b->crc % RSYNC_HTSIZE; if (ah < bh) return(-1); if (ah > bh) return(1); if (a->crc < b->crc) return(-1); if (a->crc > b->crc) return(1); return(memcmp(&a->md5[0],&b->md5[0],16)); } void sort_sums(void) { long long int i; SUMS t; void bubble_down(unsigned long long int x, unsigned long long int n) { long long int y; SUMS t; while (1) { y = R(x); if ((y < n) && (sumcmp(&sumv[x],&sumv[y]) < 0)) { if (sumcmp(&sumv[x],&sumv[y-1]) < 0) { if (sumcmp(&sumv[y],&sumv[y-1]) < 0) y --; } } else { if ((y-1 < n) && (sumcmp(&sumv[x],&sumv[y-1]) < 0)) { y --; } else { return; } } t = sumv[y]; sumv[y] = sumv[x]; sumv[x] = t; x = y; } } for (i=U(blks-1);i>=0;i--) { bubble_down(i,blks); } for (i=blks-1;i>=0;i--) { t = sumv[0]; sumv[0] = sumv[i]; sumv[i] = t; bubble_down(0,i); } } if (crctable1[128] != RSYNC_POLY) { unsigned int p; for (i=0;i<256;i++) { p = i; for (j=8;j>0;j--) p = (p >> 1) ^ ((p & 1) ? RSYNC_POLY : 0); crctable1[i] = p; } for (i=0;i<256;i++) { p = i; for (j=RSYNC_BLKSIZE;j>0;j--) p = (p >> 8) ^ crctable1[p&0xff]; crctable2[i] = p; } } i = getchar(); switch (i) { default: fprintf(stderr,"%s: protocol error, bad y subcommand %02x\n",__progname,i); exit(1); break; case EOF: fprintf(stderr,"%s: unexpected EOF (in slave_y subcommand)\n",__progname); exit(1); break; case 's': sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); sgetnts(); sscanf(&slavework[0],"%d%d%d%llu",&mode,&uid,&gid,&size); if (flag_debug) fprintf(stderr,"slave_y s %s %o %d %d %llu\n",&slavetemp[0],mode,uid,gid,size); if (flag_readonly) { printf("%d",EROFS); putchar('\0'); return; } fd = open_to_update(&slavetemp[0],mode,&rechmod); if (fd < 0) { printf("%d",errno); putchar('\0'); return; } if (flag_debug) fprintf(stderr,"slave_y s opened %s on %d\n",&slavetemp[0],fd); if ( ( !flag_noown && ( (fchown(fd,-1,gid) < 0) || (fchown(fd,uid,-1) < 0) ) ) || (lseek(fd,0,SEEK_SET) < 0) ) { printf("%d",errno); putchar('\0'); if (rechmod >= 0) fchmod(fd,rechmod); close(fd); return; } putchar('0'); putchar('\0'); fstat(fd,&stb); printf("%llu",(unsigned long long int)stb.st_size); putchar('\0'); if (flag_debug) fprintf(stderr,"slave_y s open success, size %llu\n",(unsigned long long int)stb.st_size); blks = (stb.st_size + RSYNC_BLKSIZE - 1) / RSYNC_BLKSIZE; ifill = 0; iptr = 0; if (flag_debug) fprintf(stderr,"slave_y s blks = %llu\n",blks); for (at=blks;at>0;at--) { crc = 0; md5 = md5_init(); i = (at == 1) ? stb.st_size - ((blks-1)*RSYNC_BLKSIZE) : RSYNC_BLKSIZE; while (i > 0) { if (iptr >= ifill) { ifill = read(fd,&buf[0],sizeof(buf)); if (ifill < 0) { fprintf(stderr,"%s: read error on %s: %s\n",__progname,&slavetemp[0],strerror(errno)); exit(1); } if (ifill == 0) { bzero(&buf[0],sizeof(buf)); ifill = sizeof(buf); } iptr = 0; } n = (ifill-iptr < i) ? ifill-iptr : i; i -= n; md5_process_bytes(md5,&buf[iptr],n); for (;n>0;n--) crc = (crc >> 8) ^ crctable1[(crc&0xff)^buf[iptr++]]; } buf[0] = (crc >> 24) & 0xff; buf[1] = (crc >> 16) & 0xff; buf[2] = (crc >> 8) & 0xff; buf[3] = crc & 0xff; md5_result(md5,&buf[4]); if (flag_debug) fprintf(stderr,"slave_y s blk %llu crc=%08x md5=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", blks-at,crc, buf[4],buf[5],buf[6],buf[7],buf[8],buf[9],buf[10],buf[11], buf[12],buf[13],buf[14],buf[15],buf[16],buf[17],buf[18],buf[19]); fwrite(&buf[0],1,20,stdout); } fflush(stdout); err = 0; at = 0; while (at < size) { if (flag_debug) fprintf(stderr,"slave_y s at=%llu\n",at); i = getchar(); switch (i) { default: fprintf(stderr,"%s: protocol error, bad y data subcommand %02x\n",__progname,i); exit(1); break; case EOF: fprintf(stderr,"%s: unexpected EOF (in slave_y data subcommand)\n",__progname); exit(1); break; case 'b': sgetnts(); sscanf(&slavework[0],"%llu%d",&loc,&n); if (flag_debug) fprintf(stderr,"slave_y s b %llu %d\n",loc,n); loc *= RSYNC_BLKSIZE; len = (loc+n == blks) ? stb.st_size-loc : n*RSYNC_BLKSIZE; if (! err) err = copy_within(fd,loc,stb.st_size+at,len); at += len; break; case 'd': sgetnts(); sscanf(&slavework[0],"%llu",&len); if (flag_debug) fprintf(stderr,"slave_y s d %llu\n",len); if (err) ignore_stdin(len); else err = copy_stdin(fd,stb.st_size+at,len); at += len; break; } } if (flag_debug) fprintf(stderr,"slave_y s copying back\n"); if (! err) err = copy_within(fd,stb.st_size,0,size); if (err) size = stb.st_size; if (flag_debug) fprintf(stderr,"slave_y s setting size and closing\n"); setsize(fd,size); if (rechmod >= 0) fchmod(fd,rechmod); i = close(fd); if (! err) err = i; if (flag_debug) fprintf(stderr,"slave_y s final error %d\n",err); printf("%d",err); putchar('\0'); break; case 'm': sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); fd = open(&slavetemp[0],O_RDONLY,0); if (fd < 0) { printf("%d",errno); putchar('\0'); return; } putchar('0'); putchar('\0'); fflush(stdout); sgetnts(); if (slavework[0] == 'a') { close(fd); return; } sscanf(&slavework[0],"%llu",&size); if (flag_debug) fprintf(stderr,"slave_y m %s %llu\n",&slavetemp[0],size); fstat(fd,&stb); blks = (size + RSYNC_BLKSIZE - 1) / RSYNC_BLKSIZE; sumv = malloc(blks*sizeof(SUMS)); for (len=0;len=0;i--) ht[i] = (HTENT){.n=0,.base=0}; at0 = 0; for (at=1;atn > 0) { fprintf(stderr,"slave_y m ht[%d] = %lld..%lld\n",i,e->base,e->base+e->n-1); for (j=0;jn;j++) { fprintf(stderr," blk=%u crc=%08x md5 %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", sumv[e->base+j].blk, sumv[e->base+j].crc, sumv[e->base+j].md5[ 0], sumv[e->base+j].md5[ 1], sumv[e->base+j].md5[ 2], sumv[e->base+j].md5[ 3], sumv[e->base+j].md5[ 4], sumv[e->base+j].md5[ 5], sumv[e->base+j].md5[ 6], sumv[e->base+j].md5[ 7], sumv[e->base+j].md5[ 8], sumv[e->base+j].md5[ 9], sumv[e->base+j].md5[10], sumv[e->base+j].md5[11], sumv[e->base+j].md5[12], sumv[e->base+j].md5[13], sumv[e->base+j].md5[14], sumv[e->base+j].md5[15] ); } } } } ifill = 0; iptr = 0; sumfill = 0; sumptr = 0; crc = 0; genlen = 0; printf("%llu",(unsigned long long int)stb.st_size); putchar('\0'); for (at=0;at= ifill) { ifill = read(fd,&buf[0],sizeof(buf)); if (ifill < 0) { fprintf(stderr,"%s: read error on %s: %s\n",__progname,&slavetemp[0],strerror(errno)); exit(1); } if (ifill == 0) { bzero(&buf[0],sizeof(buf)); ifill = sizeof(buf); } iptr = 0; } if (flag_debug) fprintf(stderr,"slave_y m byte %02x\n",buf[iptr]); if (sumfill < RSYNC_BLKSIZE) { if (flag_debug) fprintf(stderr,"slave_y m sumfill %d\n",sumfill); crc = (crc >> 8) ^ crctable1[(crc&0xff)^buf[iptr]]; sumbuf[sumfill++] = buf[iptr]; iptr ++; if (sumfill < RSYNC_BLKSIZE) continue; sumdrop = -1; } else { if (flag_debug) fprintf(stderr,"slave_y m sumdrop %02x\n",sumbuf[sumptr]); gen_byte(sumbuf[sumptr]); crc ^= crctable2[sumbuf[sumptr]]; crc = (crc >> 8) ^ crctable1[(crc&0xff)^buf[iptr]]; sumbuf[sumptr++] = buf[iptr]; iptr ++; if (sumptr >= RSYNC_BLKSIZE) sumptr = 0; } if (flag_debug) fprintf(stderr,"[%lld] hash %5d crc %08x\n",at,crc%RSYNC_HTSIZE,crc); e = &ht[crc%RSYNC_HTSIZE]; if (e->n > 0) { long long int l; long long int m; long long int h; if (flag_debug) fprintf(stderr,"hash table hit\n"); l = e->base - 1; h = e->base + e->n; while (h-l > 1) { m = (h + l) >> 1; if (crc <= sumv[m].crc) h = m; if (crc >= sumv[m].crc) l = m; } if (h == l) { unsigned char md5buf[16]; if (flag_debug) fprintf(stderr,"CRC hit\n"); md5 = md5_init(); md5_process_bytes(md5,&sumbuf[sumptr],RSYNC_BLKSIZE-sumptr); if (sumptr) md5_process_bytes(md5,&sumbuf[0],sumptr); md5_result(md5,&md5buf[0]); if (flag_debug) fprintf(stderr,"MD5 sum %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", md5buf[ 0], md5buf[ 1], md5buf[ 2], md5buf[ 3], md5buf[ 4], md5buf[ 5], md5buf[ 6], md5buf[ 7], md5buf[ 8], md5buf[ 9], md5buf[10], md5buf[11], md5buf[12], md5buf[13], md5buf[14], md5buf[15] ); while ((l >= 0) && (sumv[l].crc == crc)) l --; while ((h < blks) && (sumv[h].crc == crc)) h ++; while (h-l > 1) { int c; m = (h + l) >> 1; c = memcmp(&md5buf[0],&sumv[m].md5[0],16); if (c <= 0) h = m; if (c >= 0) l = m; } if (h == l) { if (flag_debug) fprintf(stderr,"hit for block %u\n",sumv[m].blk); gen_block(sumv[m].blk); sumfill = 0; sumptr = 0; crc = 0; continue; } } } else { if (flag_debug) fprintf(stderr,"hash table miss\n"); } } if (sumfill > 0) { if (sumfill < RSYNC_BLKSIZE) { for (i=0;i= RSYNC_BLKSIZE) sumptr = 0; } } } gen_flush(); break; } #undef L #undef R #undef U } static void slave_z(void) { if (! flag_gzip) { fprintf(stderr,"%s: protocol error, slave_z but no -gzip\n",__progname); exit(1); } switch (getchar()) { case 'i': { int mode; int uid; int gid; int fd; int n; int r; int left; int err; int rechmod; unsigned long long int from; unsigned long long int to; sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); sgetnts(); sscanf(&slavework[0],"%d%d%d%llu%llu",&mode,&uid,&gid,&from,&to); if (flag_debug) fprintf(stderr,"slave_z i %s %o %d %d %llu %llu\n",&slavetemp[0],mode&07777,uid,gid,from,to); if (flag_readonly) { printf("%d",EROFS); putchar('\0'); return; } fd = open_to_update(&slavetemp[0],mode,&rechmod); if (fd < 0) { printf("%d",errno); putchar('\0'); return; } if ( ( !flag_noown && ( (fchown(fd,-1,gid) < 0) || (fchown(fd,uid,-1) < 0) ) ) || (lseek(fd,from,SEEK_SET) < 0) ) { printf("%d",errno); putchar('\0'); if (rechmod >= 0) fchmod(fd,rechmod); close(fd); return; } putchar('0'); putchar('\0'); fflush(stdout); if (! flag_nosparse) fork_filter(-1,fd,0,sparsewrite); fork_filter(-1,fd,"gunzip",(char *)0); err = 0; while (1) { sgetnts(); sscanf(&slavework[0],"%d",&left); if (left < 1) break; while (left > 0) { n = (left < WORKSIZE) ? left : WORKSIZE; r = fread(&slavework[0],1,n,stdin); if (r != n) exit(0); /* what else? */ left -= r; if (err == 0) { n = write(fd,&slavework[0],r); if ((n >= 0) && (n < r)) { err = EIO; } else if (n < 0) { err = errno; } } } } if (rechmod >= 0) fchmod(fd,rechmod); close(fd); printf("%d",err); putchar('\0'); } break; case 'r': { int fd; int n; sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); if (flag_debug) fprintf(stderr,"slave_z r %s\n",&slavetemp[0]); fd = open(&slavetemp[0],O_RDONLY,0); if (fd < 0) { n = errno; printf("-1"); putchar('\0'); printf("%d",errno); putchar('\0'); } else { putchar('0'); putchar('\0'); fork_filter(fd,-1,"gzip",gzip_arg,(char *)0); while (1) { n = read(fd,&slavework[0],WORKSIZE); if (n < 0) { n = errno; printf("-1"); putchar('\0'); printf("%d",n); putchar('\0'); break; } printf("%d",n); putchar('\0'); if (n < 1) break; fwrite(&slavework[0],1,n,stdout); } close(fd); } } break; } } #if 0 static void slave_S(void) { THIS FUNCTION NOT UDPATED FOR 64-BIT FILES int fd; int cmd; int nb; int nr; long int off; long int curoff; sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); fd = open(&slavetemp[0],O_RDONLY,0); if (flag_debug) fprintf(stderr,"slave_S entering command loop\n"); curoff = 0; while (1) { fflush(stdout); cmd = getchar(); switch (cmd) { default: fprintf(stderr,"%s: protocol error, unknown slave S subcommand %d (%c)\n",__progname,cmd,cmd); exit(1); break; case EOF: fprintf(stderr,"%s: protocol error, unexpected EOF (slave_S)\n",__progname); exit(1); break; case 'c': goto out; /* aka break 2 */ break; case 'r': sgetnts(); sscanf(&slavework[0],"%d%ld",&nb,&off); if (nb > WORKSIZE) { fprintf(stderr,"%s: protocol error, nb too big (%d > %d) in slave R r\n",__progname,nb,WORKSIZE); exit(1); } if (off != curoff) lseek(fd,curoff,L_SET); curoff = off; nr = read(fd,&slavetemp[0],nb); if (nr <= 0) { printf("%d",COPYBLK_HOLE); putchar('\0'); } else { if (nr < nb) bzero(&slavetemp[nr],nb-nr); if (bcmp(&slavetemp[0],&zeroblk[0],nb)) { printf("%d",COPYBLK_DATA); putchar('\0'); fwrite(&slavetemp[0],1,nb,stdout); } else { printf("%d",COPYBLK_HOLE); putchar('\0'); } } curoff += (nr >= 0) ? nr : 0; break; } } out:; if (fd >= 0) close(fd); } static void slave_c(void) { THIS FUNCTION NOT UDPATED FOR 64-BIT FILES int mode; int uid; int gid; int size; int fd; int left; int blkleft; int blkoff; int flg; int n; int r; int tempfill; int skip; int w0; int writearg; int writerv; int finalerr; sgetnts(); slavepathcheck(); sprintf(&slavetemp[0],"%s/%s",&slavebase[0],&slavework[0]); sgetnts(); sscanf(&slavework[0],"%d%d%d%d",&mode,&uid,&gid,&size); if (flag_debug) fprintf(stderr,"slave_c %s %o %d %d %d\n",&slavetemp[0],mode&07777,uid,gid,size); if (flag_readonly) { printf("%d",EROFS); putchar('\0'); return; } fd = open(&slavetemp[0],O_WRONLY|O_CREAT|O_TRUNC,mode&07777); if (fd < 0) { printf("%d",errno); putchar('\0'); return; } if (!flag_noown && ((fchown(fd,-1,gid) < 0) || (fchown(fd,uid,-1) < 0))) { printf("%d",errno); putchar('\0'); close(fd); return; } putchar('0'); putchar('\0'); fflush(stdout); blkleft = 0; blkoff = 0; tempfill = 0; left = size; skip = 0; finalerr = 0; #define WRITE(buf,nb) do { if ((writearg=(nb)) && (writerv=write(fd,(buf),writearg)) != writearg) goto writeerr; } while (0) while ((left > 0) || (blkleft > 0)) { if (0) { writeerr:; finalerr = (writerv < 0) ? errno : EIO; close(fd); fd = -1; } if (0) { fderr:; finalerr = errno; close(fd); fd = -1; } if (blkleft < 1) { sgetnts(); if (sscanf(&slavework[0],"%d%d",&n,&flg) != 2) { fprintf(stderr,"%s: protocol error (can't read block size and type)\n",__progname); exit(1); } blkleft += n; blkoff = 0; left -= n; } switch (flg) { case COPYBLK_DATA: n = WORKSIZE - tempfill; if (blkleft < n) n = blkleft; r = fread(&slavework[tempfill],1,n,stdin); if (r != n) { fprintf(stderr,"%s: protocol error (can't read block data)\n",__progname); exit(1); } blkleft -= n; blkoff += n; if (fd < 0) continue; n += tempfill; r = 0; w0 = -1; while (n-r >= 512) { if (flag_nosparse || bcmp(&slavework[r],&zeroblk[0],512)) { if (w0 < 0) w0 = r; if (skip > 0) { if (lseek(fd,skip,L_INCR) < 0) goto fderr; skip = 0; } } else { if (w0 >= 0) { WRITE(&slavework[w0],r-w0); w0 = -1; } skip += 512; } r += 512; } if (w0 >= 0) WRITE(&slavework[w0],r-w0); tempfill = n - r; if (tempfill && (r > 0)) bcopy(&slavework[r],&slavework[0],tempfill); break; case COPYBLK_HOLE: n = blkleft; blkleft = 0; blkoff += n; if (fd < 0) continue; if (tempfill+n < 512) { bzero(&slavework[tempfill],n); tempfill += n; } else { if (flag_nosparse) { if (tempfill > 0) { bzero(&slavework[tempfill],512-tempfill); WRITE(&slavework[0],512); n -= 512 - tempfill; } while (n >= WORKSIZE) { WRITE(&zeroblk[0],WORKSIZE); n -= WORKSIZE; } if (n >= 512) { WRITE(&zeroblk[0],n&~511); n &= 511; } if (n > 0) bzero(&slavework[0],n); tempfill = n; } else { if (tempfill) { if (bcmp(&slavework[0],&zeroblk[0],tempfill)) { bzero(&slavework[tempfill],512-tempfill); if (skip > 0) { if (lseek(fd,skip,L_INCR) < 0) goto fderr; skip = 0; } WRITE(&slavework[0],512); n -= 512 - tempfill; } else { n += tempfill; } } skip += n; tempfill = skip % 512; if (tempfill) { skip -= tempfill; bzero(&slavework[0],tempfill); } } } break; } } if (tempfill > 0) { if (skip > 0) { if (lseek(fd,skip,L_INCR) < 0) { finalerr = errno; close(fd); fd = -1; } skip = 0; } if (fd >= 0) WRITE(&slavework[0],tempfill); } if (fd >= 0) { setsize(fd,size); close(fd); putchar('0'); } else { printf("%d",finalerr); } #undef WRITE putchar('\0'); } #endif static void slavealarm(int sig __attribute__((__unused__))) { exit(1); } static void slave(void) { int cmd; /* if (flag_trace) tracefilter(0,1,"slave"); if (flag_encode) xfilter(0,1,"slave"); */ printf("V%d",CURRENT_VERSION); putchar('\0'); fflush(stdout); sgetnts(); if (! slavework[0]) strcpy(&slavework[0],"."); if (flag_limitslave) { if (slavework[0] == '/') { fprintf(stderr,"%s: rooted path %s from master\n",__progname,&slavework[0]); exit(1); } slavepathcheck(); } strcpy(&slavebase[0],force_dir?:&slavework[0]); signal(SIGALRM,slavealarm); umask(0); while (1) { fflush(stdout); alarm(60*60*12); /* twelve hours of idle time -> die, master probably crashed */ cmd = getchar(); switch (cmd) { default: fprintf(stderr,"%s: protocol error, unknown slave command %d (%c)\n",__progname,cmd,cmd); exit(1); break; case EOF: exit(0); break; case 'D': slave_D(); break; case 'I': slave_I(); break; case 'K': slave_K(); break; case 'L': slave_L(); break; case 'R': slave_R(); break; case 'd': slave_d(); break; case 'f': slave_f(); break; case 'i': slave_i(); break; case 'l': slave_l(); break; case 'm': slave_m(); break; case 'n': slave_n(); break; case 'o': slave_o(); break; case 'r': slave_r(); break; case 't': slave_t(); break; case 'u': slave_u(); break; case 'y': slave_y(); break; case 'z': slave_z(); break; /* case 'S': slave_S(); break; case 'c': slave_c(); break; */ } } } #ifndef NO_NETWORK static void set_nbio(int fd, int enb) { ioctl(fd,FIONBIO,&enb); } #endif #ifndef NO_NETWORK static void ac_preselect(ACINFO *aci, fd_set *r, fd_set *w) { switch (aci->type) { default: abort(); break; case ACT_ACCEPT: { int i; for (i=aci->u.acc.nfds-1;i>=0;i--) FD_SET(aci->u.acc.fds[i],r); } break; case ACT_CONNECT: FD_SET(aci->u.conn.fd,w); break; } } #endif #ifndef NO_NETWORK static void set_tcp_nodelay(int fd) { int on; on = 1; setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,&on,sizeof(on)); } #endif #ifndef NO_NETWORK static int try_next_connect(ACINFO *aci, int verbose) { int fd; struct addrinfo *ai; char *txt; char hn[NI_MAXHOST]; char pn[NI_MAXSERV]; while (1) { ai = aci->u.conn.ai; if (ai) ai = ai->ai_next; else ai = aci->u.conn.ai0; aci->u.conn.ai = ai; if (! ai) { *aci->fdloc = -1; return(1); } fd = socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol); if (fd < 0) { if (errno != EPROTONOSUPPORT) { fprintf(stderr,"%s: socket (af %d): %s\n",__progname,ai->ai_family,strerror(errno)); } continue; } if (getnameinfo(ai->ai_addr,ai->ai_addrlen,&hn[0],NI_MAXHOST,&pn[0],NI_MAXSERV,NI_NUMERICHOST|NI_NUMERICSERV)) { asprintf(&txt,"%s/%s, can't get text string [%s] for af %d",aci->u.conn.astr,aci->u.conn.pstr,strerror(errno),ai->ai_family); } else { asprintf(&txt,"%s/%s",&hn[0],&pn[0]); } free(aci->u.conn.txt); aci->u.conn.txt = txt; set_nbio(fd,1); if (verbose) fprintf(stderr,"%s: trying %s...\n",__progname,txt); if (connect(fd,ai->ai_addr,ai->ai_addrlen) >= 0) { set_nbio(fd,0); set_tcp_nodelay(fd); *aci->fdloc = fd; return(1); } if (errno == EINPROGRESS) { aci->u.conn.fd = fd; return(0); } fprintf(stderr,"%s: connect (to %s): %s\n",__progname,txt,strerror(errno)); close(fd); verbose = 1; } } #endif #ifndef NO_NETWORK static int ac_postselect(ACINFO *aci, fd_set *r, fd_set *w) { switch (aci->type) { default: abort(); break; case ACT_ACCEPT: { int i; for (i=aci->u.acc.nfds-1;i>=0;i--) { if (FD_ISSET(aci->u.acc.fds[i],r)) { int s; struct sockaddr_storage ss; int sssize; sssize = sizeof(ss); s = accept(aci->u.acc.fds[i],(struct sockaddr *)&ss,&sssize); if (s < 0) { fprintf(stderr,"%s: accept (for %s): %s\n",__progname,aci->u.acc.txt[i],strerror(errno)); *aci->fdloc = -1; return(1); } set_tcp_nodelay(s); *aci->fdloc = s; return(1); } } } break; case ACT_CONNECT: if (FD_ISSET(aci->u.conn.fd,w)) { int e; socklen_t len; len = sizeof(e); if (getsockopt(aci->u.conn.fd,SOL_SOCKET,SO_ERROR,&e,&len) < 0) { fprintf(stderr,"%s: getsockopt SO_ERROR (for %s): %s\n",__progname,aci->u.conn.txt,strerror(errno)); *aci->fdloc = -1; return(1); } if (e == 0) { set_nbio(aci->u.conn.fd,0); set_tcp_nodelay(aci->u.conn.fd); *aci->fdloc = aci->u.conn.fd; return(1); } fprintf(stderr,"%s: connect (to %s): %s\n",__progname,aci->u.conn.txt,strerror(e)); close(aci->u.conn.fd); if (try_next_connect(aci,1)) return(1); } break; } return(0); } #endif #ifndef NO_NETWORK static void ac_free(ACINFO *aci) { switch (aci->type) { default: abort(); break; case ACT_ACCEPT: { int i; for (i=aci->u.acc.nfds-1;i>=0;i--) { close(aci->u.acc.fds[i]); free(aci->u.acc.txt[i]); } free(aci->u.acc.fds); free(aci->u.acc.txt); } break; case ACT_CONNECT: free(aci->u.conn.txt); freeaddrinfo(aci->u.conn.ai0); break; } free(aci); } #endif #ifndef NO_NETWORK static void connect_trailer(REMOTE *r) { if (r->rfd < 0) { r->errored = 1; } else { r->wfd = dup(r->rfd); } } #endif static void await_netconns(void) { #ifndef NO_NETWORK REMOTE *r; fd_set rfds; fd_set wfds; int n; while (1) { FD_ZERO(&rfds); FD_ZERO(&wfds); n = 0; for (EACH_REM(r)) { if (r->acinfo) { ac_preselect(r->acinfo,&rfds,&wfds); n ++; } } if (n == 0) break; if (select(FD_SETSIZE,&rfds,&wfds,0,0) < 0) { if (errno == EINTR) continue; fprintf(stderr,"%s: select: %s\n",__progname,strerror(errno)); exit(1); } for (EACH_REM(r)) { if (r->acinfo && ac_postselect(r->acinfo,&rfds,&wfds)) { ac_free(r->acinfo); r->acinfo = 0; connect_trailer(r); } } } #endif } static void maybe_background(void) { if (flag_bg) { int p; p = fork(); if (p < 0) { fprintf(stderr,"%s: fork: %s\n",__progname,strerror(errno)); exit(1); } if (p != 0) exit(0); } flag_bg = 0; } #ifndef NO_NETWORK static void aci_setup_accept(ACINFO *aci, const char *portstr, int *fdloc) { struct addrinfo hints; struct addrinfo *ai; struct addrinfo *ai0; int n; int *fds; char **txt; int fd; int e; char pn[NI_MAXSERV]; aci->type = ACT_ACCEPT; aci->fdloc = fdloc; hints.ai_flags = AI_PASSIVE; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; e = getaddrinfo(0,portstr,&hints,&ai0); if (e) { fprintf(stderr,"%s: %s: %s\n",__progname,portstr,gai_strerror(e)); exit(1); } if (! ai0) { fprintf(stderr,"%s: %s: successful lookup but no addresses?\n",__progname,portstr); exit(1); } n = 0; for (ai=ai0;ai;ai=ai->ai_next) n ++; fds = malloc(n*sizeof(*fds)); txt = malloc(n*sizeof(*txt)); n = 0; for (ai=ai0;ai;ai=ai->ai_next) { fd = socket(ai->ai_family,ai->ai_socktype,ai->ai_protocol); if (fd < 0) { if (errno != EPROTONOSUPPORT) { fprintf(stderr,"%s: socket (af %d): %s\n",__progname,ai->ai_family,strerror(errno)); } continue; } if (bind(fd,ai->ai_addr,ai->ai_addrlen) < 0) { e = errno; if (getnameinfo(ai->ai_addr,ai->ai_addrlen,0,0,&pn[0],NI_MAXSERV,NI_NUMERICSERV)) { fprintf(stderr,"%s: %s: af %d bind failed [%s], can't get text string [%s]\n",__progname,portstr,ai->ai_family,strerror(e),strerror(errno)); } else { fprintf(stderr,"%s: %s: af %d bind to %s failed: %s\n",__progname,portstr,ai->ai_family,&pn[0],strerror(e)); } close(fd); continue; } listen(fd,1); fds[n] = fd; if (getnameinfo(ai->ai_addr,ai->ai_addrlen,0,0,&pn[0],NI_MAXSERV,NI_NUMERICSERV)) { asprintf(&txt[n],"%s (af %d, can't get text string [%s])",portstr,ai->ai_family,strerror(errno)); } else { asprintf(&txt[n],"%s (af %d)",&pn[0],ai->ai_family); } n ++; } aci->u.acc.nfds = n; aci->u.acc.fds = fds; aci->u.acc.txt = txt; } #endif #ifndef NO_NETWORK static void setup_accept_spec(REMOTE *r, char *s) { char *s0; char *portstr; char *pathstr; char *sp; ACINFO *aci; s0 = s; s = strdup(s); portstr = s; sp = index(s,':'); if (sp == 0) { fprintf(stderr,"%s: %s: invalid :accept: spec (no third :)\n",__progname,s0); exit(1); } *sp++ = '\0'; pathstr = sp; aci = malloc(sizeof(ACINFO)); aci_setup_accept(aci,portstr,&r->rfd); r->acinfo = aci; r->directory = pathstr; } #endif #ifndef NO_NETWORK static void remote_ac_wait(ACINFO *aci) { fd_set rfds; fd_set wfds; while (1) { FD_ZERO(&rfds); FD_ZERO(&wfds); ac_preselect(aci,&rfds,&wfds); if (select(FD_SETSIZE,&rfds,&wfds,0,0) < 0) { if (errno == EINTR) continue; fprintf(stderr,"%s: select: %s\n",__progname,strerror(errno)); exit(1); } if (ac_postselect(aci,&rfds,&wfds)) return; } } #endif #ifndef NO_NETWORK static int aci_setup_connect(ACINFO *aci, const char *addrstr, const char *portstr, int *fdloc) { int e; struct addrinfo hints; struct addrinfo *ai0; aci->type = ACT_CONNECT; aci->fdloc = fdloc; hints.ai_flags = 0; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_canonname = 0; hints.ai_addr = 0; hints.ai_next = 0; e = getaddrinfo(addrstr,portstr,&hints,&ai0); if (e) { fprintf(stderr,"%s: %s/%s: %s\n",__progname,addrstr,portstr,gai_strerror(e)); exit(1); } if (! ai0) { fprintf(stderr,"%s: %s/%s: successful lookup but no addresses?\n",__progname,addrstr,portstr); exit(1); } aci->u.conn.fd = -1; aci->u.conn.ai = 0; aci->u.conn.txt = 0; aci->u.conn.ai0 = ai0; aci->u.conn.astr = addrstr; aci->u.conn.pstr = portstr; return(try_next_connect(aci,0)); } #endif #ifndef NO_NETWORK static void setup_connect_spec(REMOTE *r, char *s) { char *s0; char *addrstr; char *portstr; char *pathstr; char *sp; ACINFO *aci; s0 = s; s = strdup(s); addrstr = s; if (*addrstr == '[') { addrstr ++; sp = index(addrstr,']'); if (sp == 0) { fprintf(stderr,"%s: %s: invalid :connect: spec (unclosed [)\n",__progname,s0); exit(1); } *sp++ = '\0'; if (*sp != ':') sp = 0; } else { sp = index(s,':'); } if (sp == 0) { fprintf(stderr,"%s: %s: invalid :connect: spec (no third :)\n",__progname,s0); exit(1); } *sp++ = '\0'; portstr = sp; sp = index(sp,':'); if (sp == 0) { fprintf(stderr,"%s: %s: invalid :connect: spec (no fourth :)\n",__progname,s0); exit(1); } *sp++ = '\0'; pathstr = sp; aci = malloc(sizeof(ACINFO)); if (aci_setup_connect(aci,addrstr,portstr,&r->rfd)) { ac_free(aci); r->acinfo = 0; connect_trailer(r); } else { r->acinfo = aci; } r->directory = pathstr; } #endif static void connectremote(REMOTE *r) { int i; int p1[2]; int p2[2]; const char *newav[64]; const char **newavp; #ifndef NO_NETWORK r->acinfo = 0; #endif r->errored = 0; if (r->machine == (void *)r) { char *colon; colon = index(r->user+1,':'); if (! colon) { fprintf(stderr,"%s: %s: invalid keyword-style remote spec (no keyword terminator)\n",__progname,r->directory); exit(1); } i = colon - r->user; #ifndef NO_NETWORK if ((i == 7) && !strncmp(r->user,":accept",7)) { setup_accept_spec(r,colon+1); } else if ((i == 8) && !strncmp(r->user,":connect",8)) { setup_connect_spec(r,colon+1); } else #endif { fprintf(stderr,"%s: %s: invalid keyword-style remote spec (bad keyword %.*s)\n",__progname,r->directory,i-1,r->user+1); exit(1); } return; } if ((pipe(&p1[0]) < 0) || (pipe(&p2[0]) < 0)) { fprintf(stderr,"%s: can't create pipe: %s\n",__progname,strerror(errno)); exit(1); } switch (fork()) { case -1: fprintf(stderr,"%s: can't fork: %s\n",__progname,strerror(errno)); exit(1); break; case 0: break; default: r->wfd = p1[1]; close(p1[0]); r->rfd = p2[0]; close(p2[1]); return; break; } close(p1[1]); close(p2[0]); if (p1[0] != 0) { dup2(p1[0],0); close(p1[0]); } if (p2[1] != 1) { dup2(p2[1],1); close(p2[1]); } for (i=getdtablesize()-1;i>2;i--) close(i); if (! r->machine) { slave(); exit(0); } newavp = &newav[0]; *newavp++ = r->rsh; *newavp++ = r->machine; if (r->user) { *newavp++ = "-l"; *newavp++ = r->user; } *newavp++ = r->program ? r->program : defprogram; *newavp++ = "-R"; if (flag_debug) *newavp++ = "-debug"; if (flag_follow) *newavp++ = "-follow"; if (flag_sgiddir) *newavp++ = "-sgid-dirs"; if (flag_noown) *newavp++ = "-no-owners"; if (flag_mtimes) *newavp++ = "-mtimes"; if (flag_encode) *newavp++ = "-encode"; #ifndef NO_NETWORK if (flag_trace) *newavp++ = "-trace"; #endif if (flag_nosparse) *newavp++ = "-no-sparse"; if (flag_links) *newavp++ = "-links"; if (flag_gzip) { *newavp++ = "-gzip"; *newavp++ = gzip_arg; } *newavp++ = 0; execvp(r->rsh,(const void *)&newav[0]); printf("F%s",strerror(errno)); putchar('\0'); exit(0); } static void setupremote(REMOTE *r) { #ifndef NO_NETWORK if (flag_trace) tracefilter(r->rfd,r->wfd,r->fullarg); #endif if (flag_encode) xfilter(r->rfd,r->wfd,r->fullarg); r->rf = fdopen(r->rfd,"r"); r->wf = fdopen(r->wfd,"w"); } static void runremotes(void) { REMOTE *r; signal(SIGCHLD,reaper); for (EACH_REM(r)) connectremote(r); await_netconns(); for (EACH_REM(r)) if (r->errored) exit(1); for (EACH_REM(r)) setupremote(r); } static void setwait(REMOTE *r) { waitrem = r; } static void make_progress(void) { int p; p = progress; if (p < MAX_PROGRESS) progress = p + 1; waitrem = 0; } static void master_eof(REMOTE *r) { fprintf(stderr,"%s: unexpected eof (or error) reading from %s\n",__progname,r->fullarg); exit(1); } static void mgetnts(REMOTE *r) { int x; int c; x = 0; while (1) { setwait(r); c = getc(r->rf); make_progress(); if (c == EOF) master_eof(r); r->work[x] = c; if (c == 0) { r->worklen = x; return; } if (x < WORKSIZE-1) x ++; } } static void startup_remotes(void) { REMOTE *r; int errs; int vers; errs = 0; for (EACH_REM(r)) { int code; setwait(r); code = getc(r->rf); make_progress(); if (code == 'V') { mgetnts(r); if (sscanf(&r->work[0],"%d",&vers) != 1) { fprintf(stderr,"%s: protocol error 1 starting remote for %s\n",__progname,r->fullarg); } else if (vers != CURRENT_VERSION) { fprintf(stderr,"%s: protocol version mismatch with remote for %s\n",__progname,r->fullarg); } else { setwait(r); fputs(r->directory,r->wf); putc('\0',r->wf); make_progress(); continue; } } errs ++; if (code == 'F') { mgetnts(r); fprintf(stderr,"%s: can't start remote for %s: %s\n",__progname,r->fullarg,&r->work[0]); } else { fprintf(stderr,"%s: protocol error 2 starting remote for %s%s\n",__progname,r->fullarg,r->machine?" (permission problem?)":""); } } if (errs) exit(1); } static ENTRY *entsort(ENTRY *l) { ENTRY *a; ENTRY *b; ENTRY *t; ENTRY **lt; if (!l || !l->link) return(l); a = 0; b = 0; while (l) { t = l; l = l->link; t->link = a; a = b; b = t; } a = entsort(a); b = entsort(b); lt = &l; while (a || b) { if (a && (!b || (strcmp(a->name,b->name) < 0))) { t = a; a = a->link; } else { t = b; b = b->link; } *lt = t; lt = &t->link; } return(l); } static int samepath(const char *p1, const char *p2) { if ((p1[0] == '.') && (p1[1] == '/') && p1[2]) p1 += 2; if ((p2[0] == '.') && (p2[1] == '/') && p2[2]) p2 += 2; return(!strcmp(p1,p2)); } static LINKENTRY *link_lookup(REMOTE *r, ENTRY *e) { LINKTABLE *lt; LINKENTRY *le; lt = r->links; if (! lt) return(0); for (le=lt->links;le;le=le->link) { if ((le->dev == e->linkdev) && (le->ino == e->linkino)) return(le); } return(0); } static LINKENTRY *link_add(REMOTE *r, ENTRY *e, const char *dir) { LINKTABLE *lt; LINKENTRY *le; lt = r->links; if (! lt) { lt = NEW(LINKTABLE); lt->links = 0; r->links = lt; } le = NEW(LINKENTRY); le->dev = e->linkdev; le->ino = e->linkino; asprintf(&le->name,"%s/%s",dir,e->name); le->link = lt->links; lt->links = le; return(le); } static void link_dead(LINKENTRY *le) { free(le->name); le->name = 0; } static void link_resurrect(LINKENTRY *le, ENTRY *e, const char *dir) { asprintf(&le->name,"%s/%s",dir,e->name); } static void get_dirlist(REMOTE *r, const char *dir) { ENTRY *e; char *wp; MAPPING *map; MAPPING *mp; MAPPING **mpp; mpp = &r->mappings; map = 0; while ((mp = *mpp)) { if (samepath(dir,mp->dir)) { *mpp = mp->link; mp->link = map; map = mp; } else { mpp = &mp->link; } } r->dirlist = 0; setwait(r); fprintf(r->wf,"D%s",dir); putc('\0',r->wf); fflush(r->wf); make_progress(); while (1) { mgetnts(r); if (r->worklen == 0) break; e = newentry(); e->link = r->dirlist; r->dirlist = e; e->name = 0; mpp = ↦ while ((mp = *mpp)) { if (!strcmp(mp->from,&r->work[0])) { e->name = strdup(mp->to); *mpp = mp->link; free(mp); break; } else { mpp = &mp->link; } } if (! e->name) e->name = strdup(&r->work[0]); mgetnts(r); wp = &r->work[0]; e->mode = strtol(wp,&wp,0); e->uid = strtol(wp,&wp,0); e->gid = strtol(wp,&wp,0); if (flag_mtimes) e->mtime = strtol(wp,&wp,0); if (flag_links) { e->nlinks = strtol(wp,&wp,0); e->linkdev = strtouq(wp,&wp,0); e->linkino = strtouq(wp,&wp,0); } switch (e->mode & S_IFMT) { case S_IFDIR: e->nlinks = 1; break; case S_IFSOCK: break; case S_IFCHR: case S_IFBLK: e->rdev_maj = strtouq(wp,&wp,0); e->rdev_min = strtouq(wp,&wp,0); break; case S_IFREG: e->size = strtouq(wp,&wp,0); break; case S_IFLNK: mgetnts(r); e->softlink = strdup(&r->work[0]); break; } } r->dirlist = entsort(r->dirlist); if (flag_links) { for (e=r->dirlist;e;e=e->link) { if (e->nlinks > 1) { e->hardlink = link_lookup(r,e); if (e->hardlink) { if (e->hardlink->name) { e->firstlink = 0; } else { link_resurrect(e->hardlink,e,dir); e->firstlink = 1; } } else { e->hardlink = link_add(r,e,dir); e->firstlink = 1; } } } } while (map) { mp = map; map = mp->link; free(mp); } } static void installit(MASK, const char *, MASK *); /* forward */ static void rmcpr(MASK m, const char *dir, char *file, MASK *failmask) { MASK *fm; REMOTE *r; ENTRY *e; int err; char *addl; e = umaster->dirlist; if (MASK_TST(m,umaster->bit)) panic("rmcpr mask includes umaster"); if (strcmp(file,e->name)) panic("rmcpr file name doesn't match entry name"); { static MASK fail; fm = failmask ? failmask : &fail; } for (EACH_REM(r)) { if (MASK_TST(m,r->bit)) { setwait(r); fprintf(r->wf,"d%s/%s",dir,file); putc('\0',r->wf); fprintf(r->wf,"%d %d %d",e->mode,e->uid,e->gid); putc('\0',r->wf); fflush(r->wf); make_progress(); mgetnts(r); sscanf(&r->work[0],"%d",&err); if (err) { printf(" mkdir of %s/%s on %s failed: %s\n",dir,file,r->fullarg,strerror(err)); MASK_CLR(m,r->bit); MASK_SET(*fm,r->bit); } } } if (! MASK_ISZERO(m)) { addl = malloc(strlen(dir)+1+strlen(file)+1); sprintf(addl,"%s/%s",dir,file); get_dirlist(umaster,addl); if (umaster->dirlist) { while (umaster->dirlist) { ENTRY *et; et = umaster->dirlist; et->mode = (et->mode & perm_and) | perm_or; installit(m,addl,fm); umaster->dirlist = et->link; freeentry(et); } } free(addl); if (flag_mtimes) { for (EACH_REM(r)) { if (MASK_TST(m,r->bit)) { setwait(r); fprintf(r->wf,"u%s/%s",dir,file); putc('\0',r->wf); fprintf(r->wf,"%ld 0",e->mtime); putc('\0',r->wf); fflush(r->wf); make_progress(); mgetnts(r); sscanf(&r->work[0],"%d",&err); if (err) { printf(" utimes of %s/%s on %s failed: %s\n",dir,file,r->fullarg,strerror(err)); MASK_CLR(m,r->bit); MASK_SET(*fm,r->bit); } } } } } umaster->dirlist = e; if (! failmask) { for (EACH_REM(r)) if (MASK_TST(m,r->bit) && !MASK_TST(*fm,r->bit)) printf(" directory copy to %s succeeded\n",r->fullarg); } } static void rmmknod(MASK m, const char *dir, char *file, MASK *failmask) { REMOTE *r; ENTRY *e; e = umaster->dirlist; if (MASK_TST(m,umaster->bit)) panic("rmmknod mask includes umaster"); if (strcmp(file,e->name)) panic("rmmknod file name doesn't match entry name"); for (EACH_REM(r)) { if (MASK_TST(m,r->bit)) { int err; setwait(r); fprintf(r->wf,"n%s/%s",dir,file); putc('\0',r->wf); fprintf(r->wf,"%d %d %d %llu %llu",e->mode,e->uid,e->gid,(unsigned long long int)e->rdev_maj,(unsigned long long int)e->rdev_min); putc('\0',r->wf); fflush(r->wf); make_progress(); mgetnts(r); sscanf(&r->work[0],"%d",&err); if (err) { if (failmask) { MASK_SET(*failmask,r->bit); } else { printf(" mknod on %s failed: %s\n",r->fullarg,strerror(err)); } } else { if (! failmask) printf(" mknod on %s succeeded\n",r->fullarg); } } } } static void rmsymlink(MASK m, const char *dir, char *file, MASK *failmask) { REMOTE *r; ENTRY *e; e = umaster->dirlist; if (MASK_TST(m,umaster->bit)) panic("rmsymlink mask includes umaster"); if (strcmp(file,e->name)) panic("rmsymlink file name doesn't match entry name"); for (EACH_REM(r)) { if (MASK_TST(m,r->bit)) { int err; setwait(r); fprintf(r->wf,"l%s/%s",dir,file); putc('\0',r->wf); fprintf(r->wf,"%d %d %d",umaster->dirlist->mode,umaster->dirlist->uid,umaster->dirlist->gid); putc('\0',r->wf); fprintf(r->wf,"%s",e->softlink); putc('\0',r->wf); fflush(r->wf); make_progress(); mgetnts(r); sscanf(&r->work[0],"%d",&err); if (err) { if (failmask) { MASK_SET(*failmask,r->bit); } else { printf(" symlink on %s failed: %s\n",r->fullarg,strerror(err)); } } else { if (! failmask) printf(" symlink on %s succeeded\n",r->fullarg); } } } } #ifdef S_IFIFO static void rmmkfifo(MASK m, const char *dir, char *file, MASK *failmask) { REMOTE *r; ENTRY *e; e = umaster->dirlist; if (MASK_TST(m,umaster->bit)) panic("rmmkfifo mask includes umaster"); if (strcmp(file,e->name)) panic("rmmkfifo file name doesn't match entry name"); for (EACH_REM(r)) { if (MASK_TST(m,r->bit)) { int err; setwait(r); fprintf(r->wf,"f%s/%s",dir,file); putc('\0',r->wf); fprintf(r->wf,"%d %d %d",e->mode,e->uid,e->gid); putc('\0',r->wf); fflush(r->wf); make_progress(); mgetnts(r); sscanf(&r->work[0],"%d",&err); if (err) { if (failmask) { MASK_SET(*failmask,r->bit); } else { printf(" mkfifo on %s failed: %s\n",r->fullarg,strerror(err)); } } else { if (! failmask) printf(" mkfifo on %s succeeded\n",r->fullarg); } } } } #endif static void rmutimes(MASK m, const char *dir, char *file, MASK *failmask) { REMOTE *r; ENTRY *e; e = umaster->dirlist; if (MASK_TST(m,umaster->bit)) panic("rmutimes mask includes umaster"); if (strcmp(file,e->name)) panic("rmutimes file name doesn't match entry name"); for (EACH_REM(r)) { if (MASK_TST(m,r->bit)) { int err; setwait(r); fprintf(r->wf,"u%s/%s",dir,file); putc('\0',r->wf); fprintf(r->wf,"%ld 0",e->mtime); putc('\0',r->wf); fflush(r->wf); make_progress(); mgetnts(r); sscanf(&r->work[0],"%d",&err); if (err) { if (failmask) { MASK_SET(*failmask,r->bit); } else { printf(" utimes on %s failed: %s\n",r->fullarg,strerror(err)); } } else { if (! failmask) printf(" utimes on %s succeeded\n",r->fullarg); } } } } static void copy_n(unsigned long long int n, REMOTE *from, REMOTE *to) { int i; int j; char dbuf[8192]; while (n > 0) { i = (n < sizeof(dbuf)) ? n : sizeof(dbuf); setwait(from); j = fread(&dbuf[0],1,i,from->rf); if (i != j) master_eof(from); make_progress(); setwait(to); fwrite(&dbuf[0],1,i,to->wf); make_progress(); n -= i; } } static void rmcp(MASK m, const char *dir, const char *file, MASK *failmask, unsigned long long int fromoff, unsigned long long int tooff) { REMOTE *r; ENTRY *e; unsigned long long int left; int n; unsigned long long int off; int err; void error(void) { if (failmask) { MASK_SET(*failmask,r->bit); } else { printf(" copy to %s failed: %s\n",r->fullarg,strerror(err)); } } e = umaster->dirlist; if (MASK_TST(m,umaster->bit)) panic("rmcp mask includes umaster"); if (MASK_ISZERO(m)) return; if (strcmp(file,e->name)) panic("rmcp file name doesn't match entry name"); if (fromoff > e->size) panic("rmcp begins after eof"); if (tooff > e->size) tooff = e->size; if (flag_rsync) { for (EACH_REM(r)) { if (MASK_TST(m,r->bit)) { unsigned long long int dsize; unsigned long long int nbytes; unsigned long long int at; unsigned long long int loc; int n; int i; setwait(umaster); fprintf(umaster->wf,"ym%s/%s",dir,file); putc('\0',umaster->wf); fflush(umaster->wf); mgetnts(umaster); make_progress(); sscanf(&umaster->work[0],"%d",&err); if (err) { error(); continue; } setwait(r); fprintf(r->wf,"ys%s/%s",dir,file); putc('\0',r->wf); fprintf(r->wf,"%d %d %d %llu",e->mode,e->uid,e->gid,e->size); putc('\0',r->wf); fflush(r->wf); mgetnts(r); make_progress(); sscanf(&r->work[0],"%d",&err); if (err) { error(); putc('a',umaster->wf); putc('\0',umaster->wf); continue; } setwait(r); mgetnts(r); make_progress(); sscanf(&r->work[0],"%llu",&dsize); fprintf(umaster->wf,"%llu",dsize); putc('\0',umaster->wf); nbytes = 20 * ((dsize + RSYNC_BLKSIZE - 1) / RSYNC_BLKSIZE); copy_n(nbytes,r,umaster); fflush(umaster->wf); setwait(umaster); mgetnts(umaster); make_progress(); sscanf(&umaster->work[0],"%llu",&dsize); at = 0; while (at < dsize) { setwait(umaster); i = getc(umaster->rf); make_progress(); switch (i) { default: fprintf(stderr,"%s: bad rsync command %02x from %s\n",__progname,i,umaster->fullarg); exit(1); break; case EOF: master_eof(umaster); break; case 'b': setwait(umaster); mgetnts(umaster); make_progress(); sscanf(&umaster->work[0],"%llu %d",&loc,&n); setwait(r); fprintf(r->wf,"b%llu %d",loc,n); putc('\0',r->wf); at += n * RSYNC_BLKSIZE; break; case 'd': setwait(umaster); mgetnts(umaster); make_progress(); sscanf(&umaster->work[0],"%llu",&nbytes); setwait(r); fprintf(r->wf,"d%llu",nbytes); putc('\0',r->wf); copy_n(nbytes,umaster,r); at += nbytes; break; } } setwait(r); fflush(r->wf); mgetnts(r); make_progress(); sscanf(&r->work[0],"%d",&err); if (err) { error(); } else { if (! failmask) printf(" copy to %s succeeded\n",r->fullarg); } } } } else { for (EACH_REM(r)) { if (MASK_TST(m,r->bit)) { setwait(r); if (flag_gzip) { fprintf(r->wf,"zi%s/%s",dir,file); putc('\0',r->wf); fprintf(r->wf,"%d %d %d %llu %llu",e->mode,e->uid,e->gid,fromoff,tooff); putc('\0',r->wf); } else { fprintf(r->wf,"i%s/%s",dir,file); putc('\0',r->wf); fprintf(r->wf,"%d %d %d %llu %llu %llu",e->mode,e->uid,e->gid,e->size,fromoff,tooff); putc('\0',r->wf); } fflush(r->wf); make_progress(); mgetnts(r); sscanf(&r->work[0],"%d",&err); if (err != 0) { if (failmask) { MASK_SET(*failmask,r->bit); } else { printf(" copy to %s failed: %s\n",r->fullarg,strerror(err)); } MASK_CLR(m,r->bit); } } } if (! MASK_ISZERO(m)) { if (flag_gzip) { setwait(umaster); fprintf(umaster->wf,"zI%s/%s",dir,file); putc('\0',umaster->wf); fprintf(umaster->wf,"%llu %llu",fromoff,tooff); putc('\0',umaster->wf); fflush(umaster->wf); make_progress(); mgetnts(umaster); sscanf(&umaster->work[0],"%d",&n); if (n < 0) { mgetnts(umaster); sscanf(&umaster->work[0],"%d",&err); if (failmask) { mask_setor(failmask,m); } else { printf(" open %s failed: %s\n",umaster->fullarg,strerror(err)); } } else { while (1) { mgetnts(umaster); sscanf(&umaster->work[0],"%d",&n); for (EACH_REM(r)) if (MASK_TST(m,r->bit)) { setwait(r); fprintf(r->wf,"%d",n); putc('\0',r->wf); make_progress(); } if (n < 1) break; left = n; while (left > 0) { n = (left < WORKSIZE) ? left : WORKSIZE; setwait(umaster); fread(&umaster->work[0],1,n,umaster->rf); make_progress(); for (EACH_REM(r)) if (MASK_TST(m,r->bit)) { setwait(r); fwrite(&umaster->work[0],1,n,r->wf); make_progress(); } left -= n; } } if (n < 0) { mgetnts(umaster); sscanf(&umaster->work[0],"%d",&err); if (failmask) { mask_setor(failmask,m); } else { printf(" reading from %s failed: %s\n",umaster->fullarg,strerror(err)); } } } } else { setwait(umaster); fprintf(umaster->wf,"I%s/%s",dir,file); putc('\0',umaster->wf); fflush(umaster->wf); make_progress(); left = tooff - fromoff; off = fromoff; while (left > 0) { int flg; n = (left < WORKSIZE) ? left : WORKSIZE; setwait(umaster); fprintf(umaster->wf,"r%d %llu",n,off); putc('\0',umaster->wf); fflush(umaster->wf); make_progress(); mgetnts(umaster); if (sscanf(&umaster->work[0],"%d",&flg) != 1) { fprintf(stderr,"%s: protocol error: update master returned no flag\n",__progname); } switch (flg) { case COPYBLK_DATA: setwait(umaster); fread(&umaster->work[0],1,n,umaster->rf); make_progress(); for (EACH_REM(r)) { if (MASK_TST(m,r->bit)) { setwait(r); fprintf(r->wf,"%d %d",n,COPYBLK_DATA); putc('\0',r->wf); fwrite(&umaster->work[0],1,n,r->wf); make_progress(); } } break; case COPYBLK_HOLE: for (EACH_REM(r)) { if (MASK_TST(m,r->bit)) { setwait(r); fprintf(r->wf,"%d %d",n,COPYBLK_HOLE); putc('\0',r->wf); make_progress(); } } break; default: fprintf(stderr,"%s: update master returned unknown block code %d\n",__progname,flg); exit(1); break; } off += n; left -= n; } setwait(umaster); putc('c',umaster->wf); make_progress(); } for (EACH_REM(r)) { if (MASK_TST(m,r->bit)) { setwait(r); fflush(r->wf); make_progress(); mgetnts(r); sscanf(&r->work[0],"%d",&err); if (err) { if (failmask) { MASK_SET(*failmask,r->bit); } else { printf(" copy to %s failed: %s\n",r->fullarg,strerror(err)); } } else { if (! failmask) printf(" copy to %s succeeded\n",r->fullarg); } } } } } } static void installreg(MASK mask, const char *addl, const char *name, MASK *failmask, unsigned long long int fromoff, unsigned long long int tooff) { rmcp(mask,addl,name,failmask,fromoff,tooff); } static void installit(MASK mask, const char *addl, MASK *failmask) { ENTRY *e; e = umaster->dirlist; switch (e->mode & S_IFMT) { default: if (failmask) { *failmask = mask; } else { printf(" install failed: unknown mode %o for %s in %s:%s\n",e->mode,e->name,umaster->fullarg,addl); } break; case S_IFDIR: rmcpr(mask,addl,e->name,failmask); break; case S_IFCHR: case S_IFBLK: rmmknod(mask,addl,e->name,failmask); if (flag_mtimes) rmutimes(mask,addl,e->name,failmask); break; case S_IFREG: installreg(mask,addl,e->name,failmask,0,~0ULL); if (flag_mtimes) rmutimes(mask,addl,e->name,failmask); break; case S_IFLNK: rmsymlink(mask,addl,e->name,failmask); break; case S_IFSOCK: if (failmask) { *failmask = mask; } else { printf(" install %s/%s failed: can't copy sockets\n",addl,e->name); } break; #ifdef S_IFIFO case S_IFIFO: rmmkfifo(mask,addl,e->name,failmask); if (flag_mtimes) rmutimes(mask,addl,e->name,failmask); break; #endif } } static int rmln(REMOTE *r, const char *dir, const char *name, const char *to) { int err; setwait(r); fprintf(r->wf,"L%s/%s",dir,name); putc('\0',r->wf); fprintf(r->wf,"%s",to); putc('\0',r->wf); fflush(r->wf); make_progress(); mgetnts(r); sscanf(&r->work[0],"%d",&err); return(err); } static void install_hardlink(MASK mask, const char *addl, const char *to) { REMOTE *r; for (EACH_REM(r)) { if (MASK_TST(mask,r->bit)) { printf(" hardlink from %s %s\n",to,rmln(r,addl,umaster->dirlist->name,to)?"failed":"succeeded"); } } } static int rmrm(REMOTE *r, const char *dir, const char *name) { int err; setwait(r); fprintf(r->wf,"r%s/%s",dir,name); putc('\0',r->wf); fflush(r->wf); make_progress(); mgetnts(r); sscanf(&r->work[0],"%d",&err); return(err); } static void rmtrunc(MASK m, const char *dir, char *file, MASK *failmask) { REMOTE *r; ENTRY *e; e = umaster->dirlist; if (MASK_TST(m,umaster->bit)) panic("rmtrunc mask includes umaster"); if (strcmp(file,e->name)) panic("rmtrunc file name doesn't match entry name"); for (EACH_REM(r)) { if (MASK_TST(m,r->bit)) { int err; setwait(r); fprintf(r->wf,"t%s/%s",dir,file); putc('\0',r->wf); fprintf(r->wf,"%llu",e->size); putc('\0',r->wf); fflush(r->wf); make_progress(); mgetnts(r); sscanf(&r->work[0],"%d",&err); if (err) { if (failmask) { MASK_SET(*failmask,r->bit); } else { printf(" truncate on %s failed: %s\n",r->fullarg,strerror(err)); } } else { if (! failmask) printf(" truncate on %s succeeded\n",r->fullarg); } } } } static void rmchmod(MASK m, const char *dir, char *file, MASK *failmask) { REMOTE *r; ENTRY *e; e = umaster->dirlist; if (MASK_TST(m,umaster->bit)) panic("rmchmod mask includes umaster"); if (strcmp(file,e->name)) panic("rmchmod file name doesn't match entry name"); for (EACH_REM(r)) { if (MASK_TST(m,r->bit)) { int err; setwait(r); fprintf(r->wf,"m%s/%s",dir,file); putc('\0',r->wf); fprintf(r->wf,"%d",e->mode&07777); putc('\0',r->wf); fflush(r->wf); make_progress(); mgetnts(r); sscanf(&r->work[0],"%d",&err); if (err) { if (failmask) { MASK_SET(*failmask,r->bit); } else { printf(" chmod on %s failed: %s\n",r->fullarg,strerror(err)); } } else { if (! failmask) printf(" chmod on %s succeeded\n",r->fullarg); } } } } static void rmchown(MASK m, const char *dir, char *file, MASK *failmask) { REMOTE *r; ENTRY *e; e = umaster->dirlist; if (MASK_TST(m,umaster->bit)) panic("rmchown mask includes umaster"); if (strcmp(file,e->name)) panic("rmchown file name doesn't match entry name"); for (EACH_REM(r)) { if (MASK_TST(m,r->bit)) { int err; setwait(r); fprintf(r->wf,"o%s/%s",dir,file); putc('\0',r->wf); fprintf(r->wf,"%d %d",e->uid,e->gid); putc('\0',r->wf); fflush(r->wf); make_progress(); mgetnts(r); sscanf(&r->work[0],"%d",&err); if (err) { if (failmask) { MASK_SET(*failmask,r->bit); } else { printf(" chown on %s failed: %s\n",r->fullarg,strerror(err)); } } else { if (! failmask) printf(" chown on %s succeeded\n",r->fullarg); } } } } static char *modestring(int mode) { static char modebuf[64]; switch (mode & S_IFMT) { case S_IFDIR: modebuf[0] = 'd'; break; case S_IFCHR: modebuf[0] = 'c'; break; case S_IFBLK: modebuf[0] = 'b'; break; case S_IFREG: modebuf[0] = '-'; break; case S_IFLNK: modebuf[0] = 'l'; break; case S_IFSOCK: modebuf[0] = 's'; break; #ifdef S_IFIFO case S_IFIFO: modebuf[0] = 'p'; break; #endif default: sprintf(&modebuf[0],"?%07o",mode&07777777); return(&modebuf[0]); break; } modebuf[1] = (mode & 0400) ? 'r' : '-'; modebuf[2] = (mode & 0200) ? 'w' : '-'; switch (mode & (S_ISUID|0100)) { case 0 : modebuf[3] = '-'; break; case S_ISUID : modebuf[3] = 'S'; break; case 0100: modebuf[3] = 'x'; break; case S_ISUID|0100: modebuf[3] = 's'; break; } modebuf[4] = (mode & 040) ? 'r' : '-'; modebuf[5] = (mode & 020) ? 'w' : '-'; switch (mode & (S_ISGID|010)) { case 0 : modebuf[6] = '-'; break; case S_ISGID : modebuf[6] = 'S'; break; case 010: modebuf[6] = 'x'; break; case S_ISGID|010: modebuf[6] = 's'; break; } modebuf[7] = (mode & 04) ? 'r' : '-'; modebuf[8] = (mode & 02) ? 'w' : '-'; switch (mode & (S_ISVTX|01)) { case 0 : modebuf[9] = '-'; break; case S_ISVTX : modebuf[9] = 'T'; break; case 01: modebuf[9] = 'x'; break; case S_ISVTX|01: modebuf[9] = 't'; break; } modebuf[10] = '\0'; return(&modebuf[0]); } static void get_md5_sums(MASK m, const char *dir, const char *name, unsigned long long int from, unsigned long long int to) { REMOTE *r; for (EACH_REM(r)) { if (MASK_TST(m,r->bit)) { setwait(r); fprintf(r->wf,"K%s/%s",dir,name); putc('\0',r->wf); fprintf(r->wf,"%llu %llu",from,to); putc('\0',r->wf); fflush(r->wf); make_progress(); } } for (EACH_REM(r)) { if (MASK_TST(m,r->bit)) { setwait(r); fread(&r->work[0],1,1,r->rf); make_progress(); switch (r->work[0]) { case 'O': case 'R': mgetnts(r); r->work[0] = 0; break; case 'K': setwait(r); fread(&r->work[1],1,16,r->rf); make_progress(); r->work[0] = 1; break; } } } } static MASK compare_reg_topmask; static int compare_reg_firstset; static unsigned long long int compare_reg_size; static int compare_reg_psize; static void showhdg(void) { REMOTE *r; if (flag_check) { if (check_exit) (*check_exit)(); exit(1); } if (needhdg) { printf("%s/%s:\n",cur_dir,cur_ent); for (EACH_REM(r)) if (MASK_TST(cur_nx,r->bit)) printf(" nonexistent in %s\n",r->fullarg); needhdg = 0; } } static void compare_reg_set(MASK mask) { REMOTE *r; if (flag_debug) fprintf(stderr,"compare_reg_set mask=0x%lx\n",mask.bits[0]); if (MASK_EQ(mask,compare_reg_topmask)) return; showhdg(); if (compare_reg_firstset) { printf(" plain file %s vary",flag_md5?"checksums":"contents"); if (compare_reg_psize) printf(" for size %llu",compare_reg_size); printf(":\n"); } else { printf(" --------\n"); } compare_reg_firstset = 0; for (EACH_REM(r)) if (MASK_TST(mask,r->bit)) { printf("\t%s",r->fullarg); if (flag_md5 && !r->work[0]) printf(" (open/read failure)"); printf("\n"); } } static void compare_reg_aux(MASK mask, unsigned long long int off0) { MASK todo; MASK these; REMOTE *r; REMOTE *cmpr; unsigned long long int off; int n; if (MASK_ISZERO(mask)) panic("compare_reg_aux zero mask"); if (MASK_ONEBIT(mask)) { compare_reg_set(mask); return; } if (flag_debug) { fprintf(stderr,"compare_reg_aux starting off0=%llu mask=0x%lx\n",off0,mask.bits[0]); for (EACH_REM(r)) if (MASK_TST(mask,r->bit)) fprintf(stderr,"\t%s\n",r->fullarg); } off = off0; while (1) { if (flag_debug) fprintf(stderr,"compare_reg_aux top of loop, off=%llu\n",off); n = compare_reg_size - off; if (n > WORKSIZE) n = WORKSIZE; if (n < 1) { compare_reg_set(mask); return; } for (EACH_REM(r)) { if (MASK_TST(mask,r->bit)) { setwait(r); fprintf(r->wf,"r%d %llu",n,off); putc('\0',r->wf); fflush(r->wf); fread(&r->work[0],1,n,r->rf); make_progress(); } } off += n; if (flag_debug) fprintf(stderr,"compare_reg_aux off advanced to %llu\n",off); todo = mask; while (1) { for (EACH_REM(r)) if (MASK_TST(todo,r->bit)) break; cmpr = r; if (cmpr == 0) { panic("compare_reg_aux can't find a remote to do"); } MASK_ZERO(these); for (EACH_REM(r)) { if (MASK_TST(todo,r->bit)) { if ((r == cmpr) || !bcmp(&r->work[0],&cmpr->work[0],n)) { MASK_SET(these,r->bit); MASK_CLR(todo,r->bit); } } } if (MASK_ISZERO(todo)) break; compare_reg_aux(these,off); } mask = these; } } static void compare_reg(MASK mask, int size, int printsize) { REMOTE *r; compare_reg_topmask = mask; compare_reg_size = size; compare_reg_psize = printsize; compare_reg_firstset = 1; if (MASK_ONEBIT(mask)) return; if (flag_md5) { MASK m; MASK left; REMOTE *r2; get_md5_sums(mask,cur_dir,cur_ent,0,size); left = mask; while (! MASK_ISZERO(left)) { for (EACH_REM(r)) if (MASK_TST(left,r->bit)) break; MASK_ZERO(m); if (r->work[0]) { for (EACH_REM(r2)) if (MASK_TST(left,r2->bit) && r2->work[0] && !bcmp(&r->work[1],&r2->work[1],16)) MASK_SET(m,r2->bit); } else { MASK_SET(m,r->bit); } compare_reg_set(m); mask_setandnot(&left,m); } } else { void closedown(void) { REMOTE *r; for (EACH_REM(r)) if (MASK_TST(mask,r->bit)) { setwait(r); putc('c',r->wf); make_progress(); } } for (EACH_REM(r)) { if (MASK_TST(mask,r->bit)) { setwait(r); fprintf(r->wf,"R%s/%s",cur_dir,cur_ent); putc('\0',r->wf); fflush(r->wf); make_progress(); } } check_exit = &closedown; compare_reg_aux(mask,0); closedown(); check_exit = 0; } } static void check_reg_diff(REMOTE *mr, const char *dir, MASK *m, unsigned long long int fromsize, unsigned long long int tosize) { int left; int n; unsigned long long int off; REMOTE *r; MASK w; MASK all; if (MASK_TST(*m,mr->bit)) panic("check_reg_diff master in mask"); if (fromsize >= tosize) { if ((fromsize == 0) && (tosize == 0)) { MASK_ZERO(*m); return; } panic("check_reg_diff fromsize >= tosize"); } if (tosize > mr->dirlist->size) panic("check_reg_diff tosize > master size"); if (MASK_ISZERO(*m)) return; w = *m; if (flag_debug) fprintf(stderr,"check_reg_diff master is %s\n",mr->fullarg); MASK_ZERO(*m); for (EACH_REM(r)) { if ( MASK_TST(w,r->bit) && (ullmin(r->dirlist->size,tosize) != ullmin(mr->dirlist->size,tosize)) ) { if (flag_debug) fprintf(stderr,"check_reg_diff sees %s differs in size\n",r->fullarg); MASK_CLR(w,r->bit); MASK_SET(*m,r->bit); } } if (flag_md5) { MASK_SET(w,mr->bit); get_md5_sums(w,dir,mr->dirlist->name,fromsize,tosize); MASK_CLR(w,mr->bit); for (EACH_REM(r)) if (MASK_TST(w,r->bit) && (!mr->work[0] || !r->work[0] || bcmp(&mr->work[1],&r->work[1],16))) { if (flag_debug) fprintf(stderr,"check_reg_diff sees %s differs in checksum\n",r->fullarg); MASK_SET(*m,r->bit); } } else { void closedown(void) { REMOTE *r; for (EACH_REM(r)) if ((r == mr) || MASK_TST(all,r->bit)) { setwait(r); putc('c',r->wf); make_progress(); } } all = w; for (EACH_REM(r)) { if ((r == mr) || MASK_TST(all,r->bit)) { setwait(r); fprintf(r->wf,"R%s/%s",dir,mr->dirlist->name); putc('\0',r->wf); fflush(r->wf); make_progress(); } } check_exit = &closedown; left = tosize - fromsize; off = fromsize; while (1) { if (flag_debug) fprintf(stderr,"check_reg_diff top of loop, off=%llu\n",off); n = (left < WORKSIZE) ? left : WORKSIZE; if (n < 1) { closedown(); check_exit = 0; return; } make_progress(); for (EACH_REM(r)) { if ((r == mr) || MASK_TST(w,r->bit)) { setwait(r); fprintf(r->wf,"r%d %llu",n,off); putc('\0',r->wf); fflush(r->wf); fread(&r->work[0],1,n,r->rf); make_progress(); } } off += n; left -= n; if (flag_debug) fprintf(stderr,"check_reg_diff off advanced to %llu\n",off); for (EACH_REM(r)) { if (MASK_TST(w,r->bit)) { if (bcmp(&mr->work[0],&r->work[0],n)) { if (flag_debug) fprintf(stderr,"check_reg_diff finds %s differs\n",r->fullarg); MASK_CLR(w,r->bit); MASK_SET(*m,r->bit); } } } } } } static int wildmatch(const char *pat, const char *s, const char *s1, const char *s2) { while (1) { if (!*s && s1) { s = s1; s1 = s2; s2 = 0; } switch (*pat) { case '?': if (! *s) return(0); break; case '[': { int found; int neg; int first; const char *pp; found = 0; neg = 0; first = 1; pp = pat + 1; if (*pp == '^') { neg = 1; pp ++; } while (first || (*pp != ']')) { if ( (pp[1] == '-') && pp[2] && (pp[2] != ']') && (pp[0] <= pp[2]) ) { if ((pp[0] <= *s) && (*s <= pp[2])) found = 1; pp += 3; } else { if (pp[0] == *s) found = 1; pp ++; } first = 0; } if (! *pp) { fprintf(stderr,"%s: unclosed [ in pattern\n",__progname); exit(1); } if (neg?found:!found) return(0); pat = pp; } break; case '*': if (wildmatch(pat+1,s,s1,s2)) return(1); if (! *s) return(0); s ++; continue; break; case '\0': if (*s) return(0); return(1); break; default: if (*pat != *s) return(0); break; } pat ++; s ++; } } static int prunematch(PRUNE *p, const char *dir, const char *ent) { switch (p->type) { case PT_PATH: if (dir) { int dl; dl = strlen(dir); if ( !bcmp(dir,p->path,dl) && (p->path[dl] == '/') && !strcmp(p->path+dl+1,ent) ) return(1); } else { if (!strcmp(p->path,ent)) return(1); } break; case PT_WILD: if (wildmatch(p->path,dir?dir:ent,dir?"/":0,dir?ent:0)) return(1); break; case PT_BASE: if (wildmatch(p->path,ent,0,0)) return(1); break; } return(0); } static void newfile_init(const char *dir, char *ent) { PRUNE *p; PRUNE **pp; cur_dir = dir; cur_ent = ent; cur_pruned = 0; if ((dir[0] == '.') && !dir[1]) { pp = &prunelist; while ((p = *pp)) { if (prunematch(p,0,ent)) break; pp = &p->link; } } else { if ((dir[0] == '.') && (dir[1] == '/')) dir += 2; pp = &prunelist; while ((p = *pp)) { if (prunematch(p,dir,ent)) break; pp = &p->link; } } if (p) { cur_pruned = 1; switch (p->type) { case PT_PATH: *pp = p->link; OLD(p); break; case PT_WILD: break; } } needhdg = 1; if (prunex) cur_pruned = ! cur_pruned; } static void endfile_flush(void) { if (! needhdg) fflush(stdout); } #define ENTDIFF_TYPE 0x00000001 #define ENTDIFF_MODE 0x00000002 #define ENTDIFF_UID 0x00000004 #define ENTDIFF_GID 0x00000008 #define ENTDIFF_SIZE 0x00000010 #define ENTDIFF_RDEV 0x00000020 #define ENTDIFF_LINK 0x00000040 #define ENTDIFF_TIME 0x00000080 #define ENTDIFF_HLNK 0x00000100 static int entdiff(ENTRY *e1, ENTRY *e2) { int how; if ((e1->mode & S_IFMT) != (e2->mode & S_IFMT)) return(ENTDIFF_TYPE); how = 0; if ((e1->mode ^ e2->mode) & perm_mask) how |= ENTDIFF_MODE; if (! flag_noown) { if (e1->uid != e2->uid) how |= ENTDIFF_UID; if (e1->gid != e2->gid) how |= ENTDIFF_GID; } if (flag_mtimes && (e1->mtime != e2->mtime)) how |= ENTDIFF_TIME; if (flag_links) { if ( (e1->hardlink && e1->hardlink->name && !e1->firstlink) ? ( !(e2->hardlink && e2->hardlink->name && !e2->firstlink) || strcmp(e1->hardlink->name,e2->hardlink->name) ) : (e2->hardlink && e2->hardlink->name && !e2->firstlink) ) { how |= ENTDIFF_HLNK; } } switch (e1->mode & S_IFMT) { default: return(0); break; case S_IFDIR: if ( flag_sgiddir && (how & ENTDIFF_MODE) && ((e1->mode&05777&perm_mask) == (e2->mode&05777&perm_mask)) ) { how &= ~ENTDIFF_MODE; } break; case S_IFCHR: case S_IFBLK: if ( (e1->rdev_min != e2->rdev_min) || (e1->rdev_maj != e2->rdev_maj) ) how |= ENTDIFF_RDEV; break; case S_IFREG: if (e1->size != e2->size) how |= ENTDIFF_SIZE; break; case S_IFLNK: if (strcmp(e1->softlink,e2->softlink)) how |= ENTDIFF_LINK; how &= ~ENTDIFF_TIME; break; case S_IFSOCK: break; #ifdef S_IFIFO case S_IFIFO: break; #endif } return(how); } static void printent(REMOTE *r, const char *pref) { ENTRY *e; time_t ttmp; e = r->dirlist; switch (e->mode & S_IFMT) { default: printf("%sunknown mode %o",pref,e->mode); break; case S_IFDIR: printf("%s%s",pref,modestring(e->mode)); if (! flag_noown) printf(" (owner %d/%d)",e->uid,e->gid); break; case S_IFCHR: case S_IFBLK: printf("%s%s (",pref,modestring(e->mode)); if (! flag_noown) printf("owner %d/%d, ",e->uid,e->gid); printf("device (%llu,%llu))",e->rdev_maj,e->rdev_min); break; case S_IFREG: printf("%s%s (",pref,modestring(e->mode)); if (! flag_noown) printf("owner %d/%d, ",e->uid,e->gid); printf("size %llu)",e->size); break; case S_IFLNK: printf("%s%s (",pref,modestring(e->mode)); if (! flag_noown) printf("owner %d/%d, ",e->uid,e->gid); printf("to %s)",e->softlink); break; case S_IFSOCK: printf("%s%s",pref,modestring(e->mode)); if (! flag_noown) printf(" (owner %d/%d)",e->uid,e->gid); break; #ifdef S_IFIFO case S_IFIFO: printf("%s%s",pref,modestring(e->mode)); if (! flag_noown) printf(" (owner %d/%d)",e->uid,e->gid); break; #endif } if (flag_mtimes) { ttmp = e->mtime; printf(" mtime %.24s",ctime(&ttmp)); } if (flag_links && e->hardlink) { printf(" [hardlink"); if (! e->firstlink) printf(" to %s",e->hardlink->name); printf("]"); } printf(" in %s\n",r->fullarg); } static void union_entlist(ENTRY **listp, ENTRY *with) { ENTRY *list; ENTRY_TCONC new; etc_init(&new); list = *listp; while (list && with) { int c; c = strcmp(list->name,with->name); if (c < 0) { etc_add(&new,list); list = list->link; } else if (c > 0) { etc_add(&new,copyentry(with)); with = with->link; } else { etc_add(&new,list); list = list->link; with = with->link; } } while (with) { etc_add(&new,copyentry(with)); with = with->link; } etc_append(&new,list); etc_end(&new); *listp = new.list; } static MVLIST *sort_mvlist(MVLIST *list, int (*fn)(MVLIST *, MVLIST *)) { MVLIST *l1; MVLIST *l2; MVLIST **lpp1; MVLIST **lpp2; MVLIST *l; if (!list || !list->link) return(list); lpp1 = &l1; lpp2 = &l2; while (list) { l = list; list = l->link; *lpp1 = l; lpp1 = lpp2; lpp2 = &l->link; } *lpp1 = 0; *lpp2 = 0; lpp1 = &list; while (l1 || l2) { if (!l2 || (l1 && (*fn)(l1,l2) < 0)) { l = l1; l1 = l->link; } else { l = l2; l2 = l->link; } *lpp1 = l; lpp1 = &l->link; } *lpp1 = 0; return(list); } static int mvlist_sort_v_up(MVLIST *a, MVLIST *b) { if (a->v < b->v) return(-1); if (a->v > b->v) return(1); return(0); } static void master_dir(const char *addl) { REMOTE *r; MASK saveact; const char *last; REMOTE *nextr; MASK next; MASK dirs; ENTRY *e; ENTRY_TCONC recurse; int maxlen; int needprint; RSTACK rs; if (MASK_ISZERO(active) || MASK_ONEBIT(active)) return; saveact = active; for (EACH_REM(r)) { if (MASK_TST(active,r->bit)) { get_dirlist(r,addl); } } e = 0; for (EACH_REM(r)) union_entlist(&e,r->dirlist); rs.list = e; rs.cur = rs.list; rs.link = rstack; rstack = &rs; last = ""; maxlen = 0; etc_init(&recurse); if (flag_update) { MASK mustrm; MASK mustinstall; MASK musttrunc; MASK mustchmod; MASK mustchown; MASK mustprint; MASK mustutimes; MASK ssmaller; MASK sequal; MASK slarger; MASK cdiffs; MASK sockets; MVLIST *instlist; void add_instlist(MASK m, unsigned long int s) { MVLIST *mvl; if (! MASK_ISZERO(m)) { mvl = NEW(MVLIST); mvl->m = m; mvl->v = s & ~511UL; mvl->link = instlist; instlist = mvl; } } if (! MASK_TST(active,umaster->bit)) panic("-u master not active in master_dir"); while (1) { nextr = 0; make_progress(); for (EACH_REM(r)) if (MASK_TST(active,r->bit) && r->dirlist && ((nextr == 0) || (strcmp(r->dirlist->name,nextr->dirlist->name) < 0))) nextr = r; if (nextr == 0) break; while (rs.cur && (strcmp(rs.cur->name,nextr->dirlist->name) < 0)) rs.cur = rs.cur->link; MASK_ZERO(next); for (EACH_REM(r)) if (MASK_TST(active,r->bit) && r->dirlist && !strcmp(r->dirlist->name,nextr->dirlist->name)) MASK_SET(next,r->bit); newfile_init(addl,nextr->dirlist->name); MASK_ZERO(sockets); if (flag_ignoresockets) for (EACH_REM(r)) if (MASK_TST(next,r->bit) && ((r->dirlist->mode & S_IFMT) == S_IFSOCK)) MASK_SET(sockets,r->bit); if (! cur_pruned) { MASK_ZERO(mustprint); if (MASK_TST(next,umaster->bit)) { MASK_ZERO(mustrm); MASK_ZERO(mustchmod); MASK_ZERO(mustchown); MASK_ZERO(mustutimes); MASK_ZERO(ssmaller); MASK_ZERO(sequal); MASK_ZERO(slarger); instlist = 0; umaster->dirlist->mode = (umaster->dirlist->mode & perm_and) | perm_or; for (EACH_REM(r)) { if (r == umaster) continue; if (MASK_TST(next,r->bit)) { int how; how = entdiff(r->dirlist,umaster->dirlist); if (how & ENTDIFF_TYPE) { MASK_SET(mustrm,r->bit); } else { if (how & ENTDIFF_MODE) { MASK_SET(mustchmod,r->bit); } if (how & (ENTDIFF_UID|ENTDIFF_GID)) { MASK_SET(mustchown,r->bit); } if (how & (ENTDIFF_RDEV|ENTDIFF_LINK)) { MASK_SET(mustrm,r->bit); } if (how & ENTDIFF_SIZE) { if (umaster->dirlist->size > r->dirlist->size) MASK_SET(ssmaller,r->bit); else if (umaster->dirlist->size < r->dirlist->size) MASK_SET(slarger,r->bit); } else { MASK_SET(sequal,r->bit); } if (how & ENTDIFF_TIME) { MASK_SET(mustutimes,r->bit); } if (how & ENTDIFF_HLNK) { MASK_SET(mustrm,r->bit); } } } } switch (umaster->dirlist->mode & S_IFMT) { case S_IFREG: { MVLIST *mvl; unsigned long long int howfar; MASK tocheck; unsigned long long int s; MASK cmp; MASK diffs; MASK eqsize; MASK_ZERO(cdiffs); mustinstall = mask_or(mustrm,mask_andnot(active,next)); tocheck = ssmaller; howfar = 0; while (! MASK_ISZERO(tocheck)) { s = ~0ULL; for (EACH_REM(r)) if (MASK_TST(tocheck,r->bit) && (r->dirlist->size < s)) s = r->dirlist->size; MASK_ZERO(cmp); MASK_ZERO(eqsize); for (EACH_REM(r)) if (MASK_TST(tocheck,r->bit)) { if (r->dirlist->size >= s) MASK_SET(cmp,r->bit); if (r->dirlist->size == s) MASK_SET(eqsize,r->bit); } if (s == howfar) { if (s) panic("master_dir without advancement"); MASK_ZERO(diffs); } else { diffs = cmp; check_reg_diff(umaster,addl,&diffs,howfar,s); } add_instlist(diffs,howfar); add_instlist(mask_andnot(eqsize,diffs),s); for (EACH_REM(r)) if (MASK_TST(tocheck,r->bit) && (r->dirlist->size >= s)) MASK_SET(cmp,r->bit); howfar = s; mask_setandnot(&tocheck,cmp); } diffs = mask_or(slarger,sequal); check_reg_diff(umaster,addl,&diffs,0,umaster->dirlist->size); add_instlist(mask_or(diffs,mustinstall),0); cdiffs = mask_and(diffs,sequal); musttrunc = mask_andnot(slarger,diffs); mask_setor(&mustprint,musttrunc); instlist = sort_mvlist(instlist,&mvlist_sort_v_up); mvl = instlist; while (mvl) { if (mvl->link && (mvl->link->v == mvl->v)) { MVLIST *t; t = mvl->link; mask_setor(&mvl->m,t->m); mvl->link = t->link; free(t); } else { mask_setor(&mustinstall,mvl->m); mvl = mvl->link; } } if (flag_debug) { fprintf(stderr,"instlist:\n"); for (mvl=instlist;mvl;mvl=mvl->link) fprintf(stderr,"\tmask %08lx v %llu\n",mvl->m.bits[0],mvl->v); } } break; case S_IFSOCK: if (flag_ignoresockets) { if (flag_nodelete) MASK_ZERO(mustrm); else mustrm = mask_andnot(next,sockets); MASK_ZERO(mustinstall); MASK_ZERO(mustchmod); MASK_ZERO(mustchown); MASK_ZERO(mustutimes); MASK_ZERO(musttrunc); mask_setandnot(&mustrm,sockets); break; } /* fall through */ default: mustinstall = mask_or(mask_andnot(active,next),mustrm); mask_setor(&mustutimes,mustinstall); MASK_ZERO(musttrunc); break; } } else { if (flag_nodelete) MASK_ZERO(mustrm); else mustrm = mask_andnot(next,sockets); MASK_ZERO(mustinstall); MASK_ZERO(mustchmod); MASK_ZERO(mustchown); MASK_ZERO(mustutimes); MASK_ZERO(musttrunc); } mask_setandnot(&mustchmod,mustrm); mask_setandnot(&mustchown,mustrm); mask_setor(&mustprint,mask_or( mask_or(mustrm,mustinstall), mask_or(mustchmod,mustchown) )); if (flag_mtimes) mask_setor(&mustprint,mustutimes); if (!MASK_ISZERO(mustprint)) { showhdg(); if (MASK_TST(next,umaster->bit)) { printent(umaster," * "); } else { printf(" * nonexistent in %s\n",umaster->fullarg); } for (EACH_REM(r)) { if (r == umaster) continue; if (MASK_TST(mustprint,r->bit)) { if (MASK_TST(next,r->bit)) { printent(r," "); } else { printf(" nonexistent in %s\n",r->fullarg); } } } if (MASK_TST(next,umaster->bit) && ((umaster->dirlist->mode & S_IFMT) == S_IFREG) && !MASK_ISZERO(cdiffs)) { printf(" plain file %s differing from master copy:\n",flag_md5?"checksums":"contents"); for (EACH_REM(r)) if (MASK_TST(cdiffs,r->bit)) printf("\t%s\n",r->fullarg); } } MASK_ZERO(dirs); fflush(stdout); if (MASK_TST(next,umaster->bit)) { ENTRY *e; e = umaster->dirlist; for (EACH_REM(r)) { if (MASK_TST(mustrm,r->bit)) { if (r->dirlist->hardlink && r->dirlist->firstlink) { link_dead(r->dirlist->hardlink); } printf(" removal from %s %s\n",r->fullarg,rmrm(r,addl,e->name)?"failed":"succeeded"); } } if (! MASK_ISZERO(mustinstall)) { if (umaster->dirlist->hardlink && !umaster->dirlist->firstlink) { install_hardlink(mustinstall,addl,umaster->dirlist->hardlink->name); } else if ((umaster->dirlist->mode & S_IFMT) == S_IFREG) { MVLIST *mvl; MASK m; MASK_ZERO(m); for (mvl=instlist;mvl;mvl=mvl->link) { unsigned long long int nextsize; nextsize = mvl->link ? mvl->link->v : e->size; mask_setor(&m,mvl->m); if (flag_mtimes) mask_setor(&mustutimes,mvl->m); installreg(m,addl,e->name,0,mvl->v,nextsize); } } else { installit(mustinstall,addl,0); if (flag_mtimes) mask_setor(&mustutimes,mustinstall); } } if (! MASK_ISZERO(mustchown)) rmchown(mustchown,addl,e->name,0); if (! MASK_ISZERO(musttrunc)) rmtrunc(musttrunc,addl,e->name,0); if (! MASK_ISZERO(mustchmod)) rmchmod(mustchmod,addl,e->name,0); if (flag_mtimes && !MASK_ISZERO(mustutimes)) rmutimes(mustutimes,addl,e->name,0); if ((umaster->dirlist->mode & S_IFMT) == S_IFDIR) dirs = next; } else { for (EACH_REM(r)) if (MASK_TST(mustrm,r->bit)) printf(" removal from %s %s\n",r->fullarg,rmrm(r,addl,nextr->dirlist->name)?"failed":"succeeded"); } endfile_flush(); if (! MASK_ISZERO(dirs)) { int l; for (EACH_REM(r)) if (MASK_TST(dirs,r->bit)) break; e = r->dirlist; e->present = dirs; etc_add(&recurse,e); l = strlen(e->name); if (l > maxlen) maxlen = l; r->dirlist = r->dirlist->link; MASK_CLR(next,r->bit); } } for (EACH_REM(r)) { if (MASK_TST(next,r->bit)) { e = r->dirlist; r->dirlist = e->link; freeentry(e); } } } } else { MASK todo; MASK these; MASK ignnx; MASK sockets; MASK effnext; while (1) { nextr = 0; for (EACH_REM(r)) if (MASK_TST(active,r->bit) && r->dirlist && ((nextr == 0) || (strcmp(r->dirlist->name,nextr->dirlist->name) < 0))) nextr = r; if (nextr == 0) break; while (rs.cur && (strcmp(rs.cur->name,nextr->dirlist->name) < 0)) rs.cur = rs.cur->link; MASK_ZERO(next); MASK_ZERO(ignnx); MASK_ZERO(sockets); for (EACH_REM(r)) { if (MASK_TST(active,r->bit)) { if (r->dirlist && !strcmp(r->dirlist->name,nextr->dirlist->name)) { MASK_SET(next,r->bit); r->dirlist->mode = (r->dirlist->mode & perm_and) | perm_or; } if (r->ignnx) MASK_SET(ignnx,r->bit); } } if (flag_ignoresockets) for (EACH_REM(r)) if (MASK_TST(next,r->bit) && ((r->dirlist->mode & S_IFMT) == S_IFSOCK)) MASK_SET(sockets,r->bit); newfile_init(addl,nextr->dirlist->name); effnext = mask_andnot(next,sockets); if (!cur_pruned && !MASK_ISZERO(effnext)) { MASK_ZERO(dirs); if (! MASK_TST(effnext,nextr->bit)) { for (EACH_REM(r)) { if (MASK_TST(effnext,r->bit)) { nextr = r; break; } } if (! MASK_TST(effnext,nextr->bit)) panic("can't find non-socket representative"); } for (EACH_REM(r)) if (MASK_TST(effnext,r->bit) && ((r->dirlist->mode & S_IFMT) == S_IFDIR)) MASK_SET(dirs,r->bit); for (EACH_REM(r)) if (MASK_TST(effnext,r->bit) && entdiff(r->dirlist,nextr->dirlist)) break; cur_nx = mask_andnot(active,next); if (r || !MASK_ISZERO(mask_andnot(cur_nx,ignnx))) { showhdg(); todo = next; while (! MASK_ISZERO(todo)) { for (EACH_REM(r)) if (MASK_TST(todo,r->bit)) break; e = r->dirlist; MASK_ZERO(these); for (EACH_REM(r)) { if (MASK_TST(todo,r->bit) && (entdiff(e,r->dirlist) == 0)) { printent(r," "); MASK_SET(these,r->bit); } } mask_setandnot(&todo,these); } todo = next; for (EACH_REM(r)) if (MASK_TST(todo,r->bit) && ((r->dirlist->mode & S_IFMT) != S_IFREG)) MASK_CLR(todo,r->bit); needprint = 0; while (! MASK_ISZERO(todo)) { REMOTE *r2; for (EACH_REM(r2)) if (MASK_TST(todo,r2->bit)) break; MASK_ZERO(these); for (EACH_REM(r)) if (MASK_TST(todo,r->bit) && (r->dirlist->size == r2->dirlist->size)) MASK_SET(these,r->bit); if (! MASK_EQ(these,todo)) needprint = 1; compare_reg(these,r2->dirlist->size,needprint); mask_setandnot(&todo,these); } } else { e = nextr->dirlist; if ((e->mode & S_IFMT) == S_IFREG) { compare_reg(next,e->size,0); } } endfile_flush(); if (! MASK_ISZERO(dirs)) { int l; for (EACH_REM(r)) if (MASK_TST(dirs,r->bit)) break; e = r->dirlist; e->present = dirs; etc_add(&recurse,e); l = strlen(e->name); if (l > maxlen) maxlen = l; r->dirlist = r->dirlist->link; MASK_CLR(next,r->bit); } } for (EACH_REM(r)) { if (MASK_TST(next,r->bit)) { e = r->dirlist; r->dirlist = e->link; freeentry(e); } } } } e = rs.list; rs.list = 0; rs.cur = 0; while (e) { ENTRY *e2; e2 = e->link; freeentry(e); e = e2; } etc_end(&recurse); if (recurse.list) { int oldlen; char *newname; oldlen = strlen(addl); newname = malloc(oldlen+1+maxlen+1); bcopy(addl,newname,oldlen); newname[oldlen] = '/'; rs.list = recurse.list; for (e=recurse.list;e;e=e->link) { strcpy(newname+oldlen+1,e->name); active = e->present; rs.cur = e; master_dir(newname); } rs.list = 0; while (recurse.list) { e = recurse.list->link; freeentry(recurse.list); recurse.list = e; } free(newname); } rstack = rs.link; active = saveact; } static FILE *siginfo_outf; #define f siginfo_outf static void print_rstack(RSTACK *rs, int verbose) { ENTRY *e; int n; if (! rs) return; print_rstack(rs->link,verbose); if (! rs->list) return; if (! verbose) { fprintf(f,"%s",rs->cur->name); if ((rs->cur->mode & S_IFMT) == S_IFDIR) fprintf(f,"/"); return; } for (n=10,e=rs->list;e&&(n>0);n--,e=e->link) ; if (e) { if (! rs->cur) { while (e->link) e = e->link; fprintf(f,"%s ... %s",rs->list->name,e->name); } else { e = rs->cur; if (e == rs->list) { fprintf(f,"[ %s ]",e->name); } else if (e == rs->list->link) { fprintf(f,"%s [ %s ]",rs->list->name,e->name); } else if (e == rs->list->link->link) { fprintf(f,"%s %s [ %s ]",rs->list->name,rs->list->link->name,e->name); } else if (e == rs->list->link->link->link) { fprintf(f,"%s %s %s [ %s ]",rs->list->name,rs->list->link->name,rs->list->link->link->name,e->name); } else if (e == rs->list->link->link->link->link) { fprintf(f,"%s %s %s %s [ %s ]",rs->list->name,rs->list->link->name,rs->list->link->link->name,rs->list->link->link->link->name,e->name); } else { ENTRY *e1; ENTRY *e2; ENTRY *e3; fprintf(f,"%s ... ",rs->list->name); e1 = 0; e2 = 0; e3 = 0; for (e=rs->list;e&&(e!=rs->cur);e=e->link) { e1 = e2; e2 = e3; e3 = e; } if (e) { fprintf(f,"%s %s %s [ %s ]",e1->name,e2->name,e3->name,e->name); } else { fprintf(f,"%s [?can't find current entry]",e3->name); } } if (e) { n = 0; for (e=e->link;e;n++,e=e->link) { if (!e->link || (n < 3)) fprintf(f," %s",e->name); if ((n == 2) && e->link && e->link->link) fprintf(f," ..."); } } } } else { for (e=rs->list;e;e=e->link) { if (e == rs->cur) { fprintf(f,"[ %s ]%s",e->name,e->link?" ":""); } else { fprintf(f,"%s%s",e->name,e->link?" ":""); } } } fprintf(f,"\n"); } #undef f #ifdef SIGINFO static void dumpstate(void) { REMOTE *r; r = waitrem; if (r) { fprintf(siginfo_outf,"Blocking on %s\n",r->fullarg); } else { fprintf(siginfo_outf,"Not blocking\n"); } } static void siginfo(int sig __attribute__((__unused__))) { int p; print_rstack(rstack,1); fprintf(siginfo_outf,"\n"); p = progress; if (p > 0) progress = p-1; else dumpstate(); fflush(siginfo_outf); } #endif static void sigalrm(int sig __attribute__((__unused__))) { print_rstack(rstack,0); fprintf(siginfo_outf,"\n"); fflush(siginfo_outf); } static void runmaster(void) { REMOTE *r; int bit; bit = 0; MASK_ZERO(active); for (EACH_REM(r)) { r->bit = bit; MASK_SET(active,bit); bit ++; } if (bit > MAXREMOTES) { fprintf(stderr,"%s: too many directories specified\n",__progname); exit(1); } if (flag_update) for (EACH_REM(r)) umaster = r; rstack = 0; siginfo_outf = fopen("/dev/tty","w"); signal(SIGALRM,siginfo_outf?sigalrm:SIG_IGN); if (siginfo_outf && (tick_seconds > 0)) { struct itimerval itv; itv.it_interval.tv_sec = tick_seconds; itv.it_interval.tv_usec = 0; itv.it_value = itv.it_interval; setitimer(ITIMER_REAL,&itv,0); } progress = MAX_PROGRESS; #ifdef SIGINFO if (siginfo_outf) signal(SIGINFO,siginfo); #endif master_dir("."); for (EACH_REM(r)) { setwait(r); fflush(r->wf); make_progress(); } } static void master(void) { if (remotes == 0) { fprintf(stderr,"%s: no directories specified\n",__progname); exit(1); } if (remotes->link == 0) { fprintf(stderr,"%s: only one directory specified\n",__progname); exit(1); } runremotes(); startup_remotes(); runmaster(); } static void add_prune(char *path, int type) { PRUNE *p; if ((path[0] == '.') && (path[1] == '/')) { path += 2; while (path[0] == '/') path ++; } p = NEW(PRUNE); p->type = type; p->path = path; p->link = prunelist; prunelist = p; } #ifndef NO_NETWORK static void setup_accept(char *s) { conn_port = s; conn_addr = 0; flag_accept = 1; } #endif static void add_mapping(char *dir, char *from, char *to) { MAPPING *m; m = malloc(sizeof(MAPPING)); m->dir = dir; m->from = from; m->to = to; m->link = pendmap; pendmap = m; } int main(int, char **); int main(int ac, char **av) { remotes = 0; defprogram = av[0]; perm_mask = 07777; tick_seconds = 0; pendmap = 0; signal(SIGALRM,SIG_IGN); #define SKIP(n) do{ac-=(n);av+=(n);}while(0) while (ac > 1) { SKIP(1); if (!strcmp(av[0],"-debug") || !strcmp(av[0],"-D")) { flag_debug = 1; continue; } if (!strcmp(av[0],"-follow") || !strcmp(av[0],"-h")) { flag_follow = 1; continue; } if (!strcmp(av[0],"-sgid-dirs") || !strcmp(av[0],"-g")) { flag_sgiddir = 1; continue; } if (!strcmp(av[0],"-no-owners") || !strcmp(av[0],"-o")) { flag_noown = 1; continue; } if (!strcmp(av[0],"-R")) { flag_remote = 1; continue; } if (!strcmp(av[0],"-mtimes") || !strcmp(av[0],"-t")) { flag_mtimes = 1; continue; } if (!strcmp(av[0],"-update") || !strcmp(av[0],"-u")) { flag_update = 1; continue; } if (!strcmp(av[0],"-encode") || !strcmp(av[0],"-x")) { flag_encode = 1; continue; } #ifndef NO_NETWORK if (!strcmp(av[0],"-trace") || !strcmp(av[0],"-X")) { flag_trace = 1; continue; } #endif if (!strcmp(av[0],"-readonly") || !strcmp(av[0],"-ro")) { flag_readonly = 1; continue; } if (!strcmp(av[0],"-check")) { flag_check = 1; continue; } if (!strcmp(av[0],"-ignore-sockets")) { flag_ignoresockets = 1; continue; } if (!strcmp(av[0],"-no-sparse")) { flag_nosparse = 1; continue; } if (!strcmp(av[0],"-links")) { flag_links = 1; continue; } if (!strcmp(av[0],"-ignore-nonexistent") || !strcmp(av[0],"-inx")) { flag_nextinx = 1; continue; } if (!strcmp(av[0],"-md5")) { flag_md5 = 1; continue; } if (!strcmp(av[0],"-no-delete")) { flag_nodelete = 1; continue; } if (!strcmp(av[0],"-rsync")) { flag_rsync = 1; continue; } if (!strcmp(av[0],"-gzip") && av[1]) { gzip_arg = av[1]; flag_gzip = 1; SKIP(1); continue; } if (!strcmp(av[0],"-prune") && av[1]) { add_prune(av[1],PT_PATH); SKIP(1); continue; } if (!strcmp(av[0],"-prunewild") && av[1]) { add_prune(av[1],PT_WILD); SKIP(1); continue; } if (!strcmp(av[0],"-prunebase") && av[1]) { add_prune(av[1],PT_BASE); SKIP(1); continue; } #ifndef NO_NETWORK if (!strcmp(av[0],"-accept") && av[1]) { setup_accept(av[1]); SKIP(1); continue; } if (!strcmp(av[0],"-connect") && av[1] && av[2]) { conn_addr = av[1]; conn_port = av[2]; flag_connect = 1; SKIP(2); continue; } #endif if (!strcmp(av[0],"-prunex")) { prunex = 1; continue; } if (!strcmp(av[0],"-bg")) { flag_bg = 1; continue; } if (!strcmp(av[0],"-limit")) { flag_limitslave = 1; continue; } #ifndef NO_NETWORK if (!strcmp(av[0],"-rsh") && av[1]) { currsh = av[1]; SKIP(1); continue; } #endif if (!strcmp(av[0],"-dir") && av[1]) { newremote(av[1]); SKIP(1); continue; } if (!strcmp(av[0],"-tick") && av[1]) { tick_seconds = atoi(av[1]); SKIP(1); continue; } if (!strcmp(av[0],"-mask") && av[1]) { perm_mask = strtol(av[1],0,8) & 07777; SKIP(1); continue; } if (!strcmp(av[0],"-modemod") && av[1] && av[2]) { perm_and = (strtol(av[1],0,8) & 07777) | (~0U << 12); perm_or = strtol(av[2],0,8) & 07777; SKIP(2); continue; } if (!strcmp(av[0],"-map") && av[1] && av[2] && av[3]) { add_mapping(av[1],av[2],av[3]); SKIP(3); continue; } if (!strcmp(av[0],"-force") && av[1]) { force_dir = av[1]; SKIP(1); continue; } if (av[0][0] == '-') { fprintf(stderr,"%s: unrecognized flag %s\n",__progname,av[0]); exit(1); } newremote(av[0]); } #undef SKIP bzero(&zeroblk[0],WORKSIZE); check_exit = 0; if (flag_remote) { #ifndef NO_NETWORK int s; ACINFO *aci; #endif if (remotes) { fprintf(stderr,"%s: no directories may be given when using -R\n",__progname); exit(1); } #ifndef NO_NETWORK if (flag_accept) { aci = malloc(sizeof(ACINFO)); aci_setup_accept(aci,conn_port,&s); } else if (flag_connect) { aci = malloc(sizeof(ACINFO)); aci_setup_connect(aci,conn_addr,conn_port,&s); } else { aci = 0; } #endif maybe_background(); #ifndef NO_NETWORK if (aci) { remote_ac_wait(aci); if (s < 0) exit(1); ac_free(aci); if (s != 0) { dup2(s,0); close(s); } dup2(0,1); } #endif slave(); } else { if (flag_readonly) { fprintf(stderr,"%s: can't use -readonly except with -R\n",__progname); exit(1); } if (flag_connect) { fprintf(stderr,"%s: can't use -connect except with -R\n",__progname); exit(1); } if (flag_accept) { fprintf(stderr,"%s: can't use -accept except with -R\n",__progname); exit(1); } if (flag_bg) { fprintf(stderr,"%s: can't use -bg except with -R\n",__progname); exit(1); } master(); } exit(0); }