int ftp_msend2(ftp_host_info_t *server, FILE *control_stream, file_path_t *file_path, int file_cnt) { struct stat sbuf; char buf[512]; int fd_data; int* fd_local = NULL; int response; int i = 0; if(file_cnt == 0) return -1; fd_local = (int*)malloc(sizeof(int)*file_cnt); if(fd_local == NULL){ lib_error("[%s] malloc err!!", __FUNCTION__); return -1; } for(i=0;i<file_cnt;i++){ memset(buf, 0, sizeof(buf)); /* Connect to the data socket */ if (ftpcmd("PASV", NULL, control_stream, buf) != 227) { ftp_die("PASV", buf); } fd_data = connect_ftpdata(server, buf); if(fd_data < 0){ lib_error("[%s] connect_ftpdata err!!", __FUNCTION__); return -1; } /* get the local file */ fd_local[i] = STDIN_FILENO; if (NOT_LONE_DASH(file_path[i].local_path)) { //printf("%d: lc_path : %s, rt_path:%s\n",i, file_path[i].local_path, file_path[i].server_path); fd_local[i] = open(file_path[i].local_path, O_RDONLY, 0666); if(fd_local[i] < 0){ lib_error("[%s] open err!!", __FUNCTION__); close(fd_data); return -1; } fstat(fd_local[i], &sbuf); sprintf(buf, "ALLO %"OFF_FMT"u", sbuf.st_size); response = ftpcmd(buf, NULL, control_stream, buf); switch (response) { case 200: case 202: break; default: ftp_die("ALLO", buf); break; } } response = ftpcmd("STOR", file_path[i].server_path, control_stream, buf); switch (response) { case 125: case 150: break; default: ftp_die("STOR", buf); close(fd_local[i]); close(fd_data); goto ftp_msend_quit_error; } /* transfer the file */ if (copyfd_eof(fd_local[i], fd_data, 0) == -1) { close(fd_data); close(fd_local[i]); goto ftp_msend_quit_error; } /* close it all down */ close(fd_local[i]); close(fd_data); if (ftpcmd(NULL, NULL, control_stream, buf) != 226) { ftp_die("close", buf); } } ftpcmd("QUIT", NULL, control_stream, buf); return 0; ftp_msend_quit_error: ftpcmd("QUIT", NULL, control_stream, buf); return -1; }
int copy_file_recursive(const char *source, const char *dest) { /* This is a recursive function, try to minimize stack usage */ /* NB: each struct stat is ~100 bytes */ struct stat source_stat; struct stat dest_stat; int retval = 0; int dest_exists = 0; if (strcmp(source, ".lock") == 0) goto skip; if (stat(source, &source_stat) < 0) { perror_msg("Can't stat '%s'", source); return -1; } if (lstat(dest, &dest_stat) < 0) { if (errno != ENOENT) { perror_msg("Can't stat '%s'", dest); return -1; } } else { if (source_stat.st_dev == dest_stat.st_dev && source_stat.st_ino == dest_stat.st_ino ) { error_msg("'%s' and '%s' are the same file", source, dest); return -1; } dest_exists = 1; } if (S_ISDIR(source_stat.st_mode)) { DIR *dp; struct dirent *d; if (dest_exists) { if (!S_ISDIR(dest_stat.st_mode)) { error_msg("Target '%s' is not a directory", dest); return -1; } /* race here: user can substitute a symlink between * this check and actual creation of files inside dest */ } else { /* Create DEST */ mode_t mode = source_stat.st_mode; /* Allow owner to access new dir (at least for now) */ mode |= S_IRWXU; if (mkdir(dest, mode) < 0) { perror_msg("Can't create directory '%s'", dest); return -1; } } /* Recursively copy files in SOURCE */ dp = opendir(source); if (dp == NULL) { retval = -1; goto ret; } while (retval == 0 && (d = readdir(dp)) != NULL) { char *new_source, *new_dest; if (dot_or_dotdot(d->d_name)) continue; new_source = concat_path_file(source, d->d_name); new_dest = concat_path_file(dest, d->d_name); if (copy_file_recursive(new_source, new_dest) < 0) retval = -1; free(new_source); free(new_dest); } closedir(dp); goto ret; } if (S_ISREG(source_stat.st_mode)) { int src_fd; int dst_fd; mode_t new_mode; src_fd = open(source, O_RDONLY); if (src_fd < 0) { perror_msg("Can't open '%s'", source); return -1; } /* Do not try to open with weird mode fields */ new_mode = source_stat.st_mode; // security problem versus (sym)link attacks // dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode); /* safe way: */ dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); if (dst_fd < 0) { close(src_fd); return -1; } if (copyfd_eof(src_fd, dst_fd, COPYFD_SPARSE) == -1) retval = -1; close(src_fd); /* Careful: do check that buffered writes succeeded... */ if (close(dst_fd) < 0) { perror_msg("Error writing to '%s'", dest); retval = -1; } else { /* (Try to) copy atime and mtime */ struct timeval atime_mtime[2]; atime_mtime[0].tv_sec = source_stat.st_atime; // note: if "st_atim.tv_nsec" doesn't compile, try "st_atimensec": atime_mtime[0].tv_usec = source_stat.st_atim.tv_nsec / 1000; atime_mtime[1].tv_sec = source_stat.st_mtime; atime_mtime[1].tv_usec = source_stat.st_mtim.tv_nsec / 1000; // note: can use utimensat when it is more widely supported: utimes(dest, atime_mtime); } goto ret; } /* Neither dir not regular file: skip */ skip: log_warning("Skipping '%s'", source); ret: return retval; }
int ftp_send(ftp_host_info_t *server, FILE *control_stream, const char *server_path, char *local_path) { struct stat sbuf; char buf[512]; int fd_data; int fd_local; int response; /* Connect to the data socket */ if (ftpcmd("PASV", NULL, control_stream, buf) != 227) { ftp_die("PASV", buf); } fd_data = connect_ftpdata(server, buf); if(fd_data < 0){ lib_error("[%s] connect_ftpdata err!!", __FUNCTION__); return -1; } /* get the local file */ fd_local = STDIN_FILENO; if (NOT_LONE_DASH(local_path)) { fd_local = open(local_path, O_RDONLY, 0666); if(fd_local < 0){ lib_error("[%s] open err!!", __FUNCTION__); close(fd_data); return -1; } fstat(fd_local, &sbuf); sprintf(buf, "ALLO %"OFF_FMT"u", sbuf.st_size); response = ftpcmd(buf, NULL, control_stream, buf); switch (response) { case 200: case 202: break; default: ftp_die("ALLO", buf); break; } } response = ftpcmd("STOR", server_path, control_stream, buf); switch (response) { case 125: case 150: break; default: ftp_die("STOR", buf); close(fd_local); close(fd_data); return -1; } /* transfer the file */ if (copyfd_eof(fd_local, fd_data, 0) == -1) { close(fd_data); close(fd_local); return -1; } /* close it all down */ close(fd_local); close(fd_data); if (ftpcmd(NULL, NULL, control_stream, buf) != 226) { ftp_die("close", buf); } //ftpcmd("NOOP", NULL, control_stream, buf); ftpcmd("QUIT", NULL, control_stream, buf); return 0; }