* important case. Some systems may not have d_type.
**/
struct file_struct *make_file(char *fname, struct file_list *flist,
- int filter_level)
+ STRUCT_STAT *stp, int filter_level)
{
static char *lastdir;
static int lastdir_len = -1;
memset(sum, 0, SUM_LENGTH);
- if (readlink_stat(thisname, &st, linkname) != 0) {
+ if (stp && S_ISDIR(stp->st_mode))
+ st = *stp; /* Needed for "symlink/." with --relative. */
+ else if (readlink_stat(thisname, &st, linkname) != 0) {
int save_errno = errno;
/* See if file is excluded before reporting an error. */
if (filter_level != NO_FILTERS
}
static struct file_struct *send_file_name(int f, struct file_list *flist,
- char *fname, unsigned short base_flags)
+ char *fname, STRUCT_STAT *stp,
+ unsigned short base_flags)
{
struct file_struct *file;
- file = make_file(fname, flist, f == -2 ? SERVER_FILTERS : ALL_FILTERS);
+ file = make_file(fname, flist, stp,
+ f == -2 ? SERVER_FILTERS : ALL_FILTERS);
if (!file)
return NULL;
continue;
}
- send_file_name(f, flist, fbuf, 0);
+ send_file_name(f, flist, fbuf, NULL, 0);
}
fbuf[len] = '\0';
}
len = strlen(fbuf);
- if (!len || fbuf[len - 1] == '/') {
+ if (relative_paths) {
+ /* We clean up fbuf below. */
+ is_dot_dir = 0;
+ } else if (!len || fbuf[len - 1] == '/') {
if (len == 2 && fbuf[0] == '.') {
/* Turn "./" into just "." rather than "./." */
fbuf[1] = '\0';
fn = p + 1;
} else
fn = fbuf;
- } else if ((p = strstr(fbuf, "/./")) != NULL) {
- *p = '\0';
- if (p == fbuf)
- dir = "/";
- else
- dir = fbuf;
- len -= p - fbuf + 3;
- fn = p + 3;
- } else
- fn = fbuf;
+ } else {
+ if ((p = strstr(fbuf, "/./")) != NULL) {
+ *p = '\0';
+ if (p == fbuf)
+ dir = "/";
+ else
+ dir = fbuf;
+ len -= p - fbuf + 3;
+ fn = p + 3;
+ } else
+ fn = fbuf;
+ /* Get rid of trailing "/" and "/.". */
+ while (len) {
+ if (fn[len - 1] == '/') {
+ is_dot_dir = 1;
+ if (!--len && !dir) {
+ len++;
+ break;
+ }
+ }
+ else if (len >= 2 && fn[len - 1] == '.'
+ && fn[len - 2] == '/') {
+ is_dot_dir = 1;
+ if (!(len -= 2) && !dir) {
+ len++;
+ break;
+ }
+ } else
+ break;
+ }
+ fn[len] = '\0';
+ /* Reject a ".." dir in the active part of the path. */
+ for (p = fn; (p = strstr(p, "..")) != NULL; p += 2) {
+ if ((p[2] == '/' || p[2] == '\0')
+ && (p == fn || p[-1] == '/')) {
+ rprintf(FERROR,
+ "found \"..\" dir in relative path: %s\n",
+ fbuf);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ }
+ }
if (!*fn) {
len = 1;
xfer_dirs = 1;
while ((slash = strchr(slash+1, '/')) != 0) {
*slash = '\0';
- send_file_name(f, flist, fbuf, 0);
+ send_file_name(f, flist, fbuf, NULL, 0);
*slash = '/';
}
copy_links = save_copy_links;
if (recurse || (xfer_dirs && is_dot_dir)) {
struct file_struct *file;
- if ((file = send_file_name(f, flist, fbuf, XMIT_TOP_DIR)))
+ file = send_file_name(f, flist, fbuf, &st, XMIT_TOP_DIR);
+ if (file)
send_if_directory(f, flist, file, fbuf, len);
} else
- send_file_name(f, flist, fbuf, 0);
+ send_file_name(f, flist, fbuf, &st, 0);
if (olddir[0]) {
flist_dir = NULL;