#include "safeio.h" #include "socket.h" #include "stringf.h" #include #include #include static string get_string(FILE *f,const char *fn,const char *what) { char buf[PATH_MAX]; char *got=fgets(buf,sizeof(buf),f); if (got!=buf) fatal("Error reading %s from %s: %m",what,fn); char *s(strchr(buf,'\n')); if (!s) fatal("Stored %s in %s is not newline-terminated",what,fn); *s=0; assert(!s[1]); return buf; } static void last_stored_write(const char *last_stored_fn,const string &filename,uint64_t offset) { std::string last_tmp_fn(std::string(last_stored_fn)+".new"); int last_tmp_fd(open(last_tmp_fn.c_str(),O_WRONLY|O_CREAT|O_TRUNC,0644)); if (last_tmp_fd==-1) fatal("Error storing filename to %s: %m",last_tmp_fn.c_str()); write_safe(last_tmp_fd,filename.c_str(),filename.length()); write_safe(last_tmp_fd,'\n'); string offsetstr(stringf("%zu",(size_t)offset)); write_safe(last_tmp_fd,offsetstr.c_str(),offsetstr.length()); write_safe(last_tmp_fd,'\n'); if (close(last_tmp_fd)) fatal("Error closing %s: %m",last_tmp_fn.c_str()); if (rename(last_tmp_fn.c_str(),last_stored_fn)) fatal("Error renaming %s->%s: %m",last_tmp_fn.c_str(),last_stored_fn); } static string my_basename(const string &path) { size_t slash(path.find_last_of('/')); if (slash!=string::npos) return path.substr(slash+1); return path; } int main(int argc,char **argv) { if (argc!=1+3) fatal("streamfer-client : "); int server_fd(socket_connect(string(argv[1]),0/*retries*/)); string pattern(argv[2]); write_safe(server_fd,pattern); string last_found; const char *last_stored_fn(argv[3]); FILE *last_stored_f(fopen(last_stored_fn,"r")); uint64_t offset(0); if (!last_stored_f) { if (errno!=ENOENT) fatal("Error opening filename from %s: %m",last_stored_fn); } else { last_found= get_string(last_stored_f,last_stored_fn,"filename") ; string offsetstr(get_string(last_stored_f,last_stored_fn,"offset" )); char *end; errno=0; unsigned long ul(strtoul(offsetstr.c_str(),&end,0)); if (errno||(end&&*end)) fatal("Error converting offset from %s: %s",last_stored_fn,offsetstr.c_str()); offset=ul; int gotint(fgetc(last_stored_f)); if (gotint!=EOF) fatal("Stored filename in %s has excessive data after filename %s and offset %s",last_stored_fn,last_found.c_str(),offsetstr.c_str()); int err(fclose(last_stored_f)); assert(!err); } write_safe(server_fd,last_found); write_safe(server_fd,offset); { struct stat statbuf; string last_found_basename(my_basename(last_found)); if (stat(last_found_basename.c_str(),&statbuf)) { static const struct timespec mtim_zero{}; write_safe(server_fd,mtim_zero); } else write_safe(server_fd,statbuf.st_mtim); } string last_got; try { read_safe(server_fd,last_got); } catch (SafeIOError) { fatal("SafeIOError reading from the server %s",argv[1]); } if (last_got.empty()) { warning("No more files to transfer"); exit(EXIT_SUCCESS); } struct timespec mtim; read_safe(server_fd,mtim); int file_fd; string file_name(my_basename(last_got));; if (last_found==last_got) { file_fd=open(file_name.c_str(),O_WRONLY); if (file_fd==-1) fatal("Error opening for write %s: %m",file_name.c_str()); off_t got(lseek(file_fd,offset,SEEK_SET)); if ((uint64_t)got!=offset) fatal("Error seeking in %s to %zu, got: %zu",file_name.c_str(),(size_t)offset,(size_t)got); } else { offset=0; file_fd=open(file_name.c_str(),O_WRONLY|O_CREAT,0644); if (file_fd==-1) fatal("Error creating %s: %m",file_name.c_str()); } last_stored_write(last_stored_fn,last_got,offset); uint64_t transferred(transfer(server_fd,"server fd",file_fd,file_name.c_str())); if (!transferred) { struct stat statbuf; int err(fstat(file_fd,&statbuf)); assert(!err); if (memcmp(&mtim,&statbuf.st_mtim,sizeof(mtim))==0) return EXIT_FAILURE; } offset+=transferred; struct timespec mtim2[2]; mtim2[0]=mtim; // atime mtim2[1]=mtim; // mtime if (futimens(file_fd,mtim2)) fatal("Error setting timestamp of %s: %m",file_name.c_str()); if (close(file_fd)) fatal("Error closing %s: %m",file_name.c_str()); last_stored_write(last_stored_fn,last_got,offset); return EXIT_SUCCESS; }