X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/4c36ddbeecdde407c870109d70527640ca127ace..37863201ad549b11ce70ecfb1e348165637f2e44:/util.c diff --git a/util.c b/util.c index b95e0b45..70743a66 100644 --- a/util.c +++ b/util.c @@ -24,15 +24,33 @@ */ #include "rsync.h" -int num_waiting(int fd) +/**************************************************************************** +Set a fd into nonblocking mode. Uses POSIX O_NONBLOCK if available, +else +if SYSV use O_NDELAY +if BSD use FNDELAY +****************************************************************************/ +int set_nonblocking(int fd) { - int len=0; - ioctl(fd,FIONREAD,&len); - return(len); + int val; +#ifdef O_NONBLOCK +#define FLAG_TO_SET O_NONBLOCK +#else +#ifdef SYSV +#define FLAG_TO_SET O_NDELAY +#else /* BSD */ +#define FLAG_TO_SET FNDELAY +#endif +#endif + + if((val = fcntl(fd, F_GETFL, 0)) == -1) + return -1; + val |= FLAG_TO_SET; + return fcntl( fd, F_SETFL, val); +#undef FLAG_TO_SET } - /* this is taken from CVS */ int piped_child(char **command,int *f_in,int *f_out) { @@ -80,6 +98,9 @@ int piped_child(char **command,int *f_in,int *f_out) *f_in = from_child_pipe[0]; *f_out = to_child_pipe[1]; + + set_nonblocking(*f_in); + set_nonblocking(*f_out); return pid; } @@ -206,7 +227,7 @@ int create_directory_path(char *fname) derived from GNU C's cccp.c. */ -int full_write(int desc, char *ptr, int len) +static int full_write(int desc, char *ptr, int len) { int total_written; @@ -273,7 +294,7 @@ int copy_file(char *source, char *dest, mode_t mode) } ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode); - if (ofd < 0) { + if (ofd == -1) { rprintf(FERROR,"open %s: %s\n", dest,strerror(errno)); close(ifd); @@ -553,3 +574,189 @@ int slprintf(char *str, int n, char *format, ...) return ret; } + +void *Realloc(void *p, int size) +{ + if (!p) return (void *)malloc(size); + return (void *)realloc(p, size); +} + + +void clean_fname(char *name) +{ + char *p; + int l; + int modified = 1; + + if (!name) return; + + while (modified) { + modified = 0; + + if ((p=strstr(name,"/./"))) { + modified = 1; + while (*p) { + p[0] = p[2]; + p++; + } + } + + if ((p=strstr(name,"//"))) { + modified = 1; + while (*p) { + p[0] = p[1]; + p++; + } + } + + if (strncmp(p=name,"./",2) == 0) { + modified = 1; + do { + p[0] = p[2]; + } while (*p++); + } + + l = strlen(p=name); + if (l > 1 && p[l-1] == '/') { + modified = 1; + p[l-1] = 0; + } + } +} + + +static char curr_dir[MAXPATHLEN]; + +/* like chdir() but can be reversed with pop_dir() if save is set. It + is also much faster as it remembers where we have been */ +char *push_dir(char *dir, int save) +{ + char *ret = curr_dir; + static int initialised; + + if (!initialised) { + initialised = 1; + getcwd(curr_dir, sizeof(curr_dir)-1); + } + + if (chdir(dir)) return NULL; + + if (save) { + ret = strdup(curr_dir); + } + + if (*dir == '/') { + strlcpy(curr_dir, dir, sizeof(curr_dir)-1); + } else { + strlcat(curr_dir,"/", sizeof(curr_dir)-1); + strlcat(curr_dir,dir, sizeof(curr_dir)-1); + } + + clean_fname(curr_dir); + + return ret; +} + +/* reverse a push_dir call */ +int pop_dir(char *dir) +{ + int ret; + + ret = chdir(dir); + if (ret) { + free(dir); + return ret; + } + + strlcpy(curr_dir, dir, sizeof(curr_dir)-1); + + free(dir); + + return 0; +} + +/* we need to supply our own strcmp function for file list comparisons + to ensure that signed/unsigned usage is consistent between machines. */ +int u_strcmp(const char *cs1, const char *cs2) +{ + const uchar *s1 = (uchar *)cs1; + const uchar *s2 = (uchar *)cs2; + + while (*s1 && *s2 && (*s1 == *s2)) { + s1++; s2++; + } + + return (int)*s1 - (int)*s2; +} + +static OFF_T last_ofs; + +void end_progress(void) +{ + extern int do_progress, am_server; + + if (do_progress && !am_server) { + rprintf(FINFO,"\n"); + } + last_ofs = 0; +} + +void show_progress(OFF_T ofs, OFF_T size) +{ + extern int do_progress, am_server; + + if (do_progress && !am_server) { + if (ofs > last_ofs + 1000) { + int pct = (int)((100.0*ofs)/size); + rprintf(FINFO,"%.0f (%d%%)\r", (double)ofs, pct); + last_ofs = ofs; + } + } +} + +/* determine if a symlink points outside the current directory tree */ +int unsafe_symlink(char *dest, char *src) +{ + char *tok; + int depth = 0; + + /* all absolute and null symlinks are unsafe */ + if (!dest || !(*dest) || (*dest == '/')) return 1; + + src = strdup(src); + if (!src) out_of_memory("unsafe_symlink"); + + /* find out what our safety margin is */ + for (tok=strtok(src,"/"); tok; tok=strtok(NULL,"/")) { + if (strcmp(tok,"..") == 0) { + depth=0; + } else if (strcmp(tok,".") == 0) { + /* nothing */ + } else { + depth++; + } + } + free(src); + + /* drop by one to account for the filename portion */ + depth--; + + dest = strdup(dest); + if (!dest) out_of_memory("unsafe_symlink"); + + for (tok=strtok(dest,"/"); tok; tok=strtok(NULL,"/")) { + if (strcmp(tok,"..") == 0) { + depth--; + } else if (strcmp(tok,".") == 0) { + /* nothing */ + } else { + depth++; + } + /* if at any point we go outside the current directory then + stop - it is unsafe */ + if (depth < 0) break; + } + + free(dest); + return (depth < 0); +}