/** * Perform FILEINFO processing specific to a regular file in sync mode * Returns 1 if a COMPLETE was sent in response, 0 otherwise */ int handle_fileinfo_sync(struct group_list_t *group) { stat_struct statbuf; if (lstat_func(group->fileinfo.filepath, &statbuf) != -1) { // If source is newer, skip // If source is older, overwrite // If timestamps same, skip if sizes are also same int skip; if (group->fileinfo.tstamp < statbuf.st_mtime) { skip = 1; } else if (group->fileinfo.tstamp > statbuf.st_mtime) { skip = 0; } else if (S_ISREG(statbuf.st_mode) && (statbuf.st_size == group->fileinfo.size)) { skip = 1; } else { skip = 0; } if (skip) { glog2(group, "skipping file, in sync"); early_complete(group, COMP_STAT_SKIPPED, 0); return 1; } else { glog2(group, "overwriting out of sync file"); group->fileinfo.comp_status = COMP_STAT_OVERWRITE; if (group->sync_preview) { glog2(group, "Sync preview mode, skipping receive"); early_complete(group, COMP_STAT_OVERWRITE, 0); return 1; } if (!tempfile) { move_to_backup(group); } } } else { glog2(group, "copying new file"); if (group->sync_preview) { glog2(group, "Sync preview mode, skipping receive"); early_complete(group, COMP_STAT_NORMAL, 0); return 1; } if (!tempfile) { move_to_backup(group); } } return 0; }
/** * Perform FILEINFO processing specific to a delete command */ void handle_fileinfo_delete(struct group_list_t *group) { if (!group->sync_preview) { move_to_backup(group); } early_complete(group, COMP_STAT_NORMAL, 0); }
/** * Process an incoming FILEINFO message. * Expected in the middle of a group with no current file. */ void handle_fileinfo(struct group_list_t *group, const unsigned char *message, unsigned meslen, struct timeval rxtime) { stat_struct statbuf; int found_dir; if (!read_fileinfo(group, message, meslen, rxtime)) { return; } glog2(group, "Name of file to receive: %s", group->fileinfo.name); switch (group->fileinfo.ftype) { case FTYPE_REG: glog2(group, "Bytes: %s, Blocks: %d, Sections: %d", printll(group->fileinfo.size), group->fileinfo.blocks, group->fileinfo.sections); glog3(group, "small section size: %d, " "big section size: %d, # big sections: %d", group->fileinfo.secsize_small, group->fileinfo.secsize_big, group->fileinfo.big_sections); break; case FTYPE_DIR: glog2(group, "Empty directory"); break; case FTYPE_LINK: glog2(group, "Symbolic link to %s", group->fileinfo.linkname); break; case FTYPE_DELETE: glog2(group, "Deleting file/directory"); break; case FTYPE_FREESPACE: glog2(group, "Get free space for path"); break; default: glog1(group, "Invalid file type: %d", group->fileinfo.ftype); send_abort(group, "Invalid file type"); return; } if (!setup_dest_file(group)) { // A rejected file is still a success because we responded with a // COMPLETE with status=rejected instead of with an ABORT return; } // Make sure the path to the destination file exists and // remove or back up any existing file if (!create_path_to_file(group, group->fileinfo.filepath)) { glog0(group, "Error creating path to data file"); early_complete(group, COMP_STAT_REJECTED, 0); return; } found_dir = 0; if (tempfile && !group->sync_preview) { clear_path(group->fileinfo.temppath, group); } if ((group->fileinfo.ftype != FTYPE_DELETE) || (group->fileinfo.ftype != FTYPE_FREESPACE)) { // Don't do path checks for metafile commands } else if (lstat_func(group->fileinfo.filepath, &statbuf) != -1) { glog3(group, "checking existing file"); if ((group->fileinfo.ftype != FTYPE_DIR) || !S_ISDIR(statbuf.st_mode)) { if ((group->fileinfo.ftype != FTYPE_REG) || !S_ISREG(statbuf.st_mode) || ((!group->restart) && (!group->sync_mode))) { // Don't clear/backup if we're receiving a regular file // and we're in either restart mode or sync mode glog3(group, "calling move_to_backup"); if (!tempfile) { move_to_backup(group); } } } else { glog3(group, "found dir"); found_dir = 1; } } else if (errno != ENOENT) { gsyserror(group, "Error checking file %s",group->fileinfo.filepath); } switch (group->fileinfo.ftype) { case FTYPE_REG: handle_fileinfo_regular(group); break; case FTYPE_DIR: handle_fileinfo_dir(group, found_dir); break; case FTYPE_LINK: handle_fileinfo_link(group); break; case FTYPE_DELETE: handle_fileinfo_delete(group); break; case FTYPE_FREESPACE: handle_fileinfo_freespace(group); break; default: glog0(group, "Error handling FILEINFO: shouldn't get here!"); } }
/** * Clean up a group list entry. Close the file if open, * free malloc'ed structures, drop the multicast group * (if no one else is using it) and free the slot. */ void file_cleanup(int listidx, int abort) { if (group_list[listidx].fileinfo.fd >= 0) { close(group_list[listidx].fileinfo.fd); group_list[listidx].fileinfo.fd = -1; if (tempfile) { move_to_backup(listidx); if (rename(group_list[listidx].fileinfo.temppath, group_list[listidx].fileinfo.filepath) == -1) { syserror(group_list[listidx].group_id, group_list[listidx].file_id, "Couldn't rename from %s to %s", group_list[listidx].fileinfo.temppath, group_list[listidx].fileinfo.filepath); } } if (group_list[listidx].fileinfo.tstamp) { utim_buf utbuf; utbuf.actime = group_list[listidx].fileinfo.tstamp; utbuf.modtime = group_list[listidx].fileinfo.tstamp; if (utime(group_list[listidx].fileinfo.filepath, &utbuf) == -1) { syserror(group_list[listidx].group_id, group_list[listidx].file_id, "utime failed"); } } } if ((group_list[listidx].version == UFTP_V2_VER) || abort || (group_list[listidx].file_id == 0)) { if ((group_list[listidx].multi.s_addr != 0) && !other_mcast_users(listidx) && group_list[listidx].multi_join) { if (server_count > 0) { multicast_leave(listener, group_list[listidx].group_id, &group_list[listidx].multi, m_interface, interface_count, server_keys, server_count); if (has_proxy) { multicast_leave(listener, group_list[listidx].group_id, &group_list[listidx].multi, m_interface, interface_count, &proxy_info, 1); } } else { multicast_leave(listener, group_list[listidx].group_id, &group_list[listidx].multi, m_interface, interface_count, NULL, 0); } } if (group_list[listidx].serverkey) { free_RSA_key(group_list[listidx].serverkey); } if (group_list[listidx].restartinfo && (strcmp(group_list[listidx].restartinfo->name, ""))) { // We have unused restart info from the last run. // Chalk this up as a loss and delete the data file char filepath[MAXPATHNAME]; snprintf(filepath, sizeof(filepath), "%s%c_group_%08X%c%s", tempdir, PATH_SEP, group_list[listidx].group_id, PATH_SEP, group_list[listidx].restartinfo->name); unlink(filepath); } if (abort) { write_restart_file(listidx); } free(group_list[listidx].fileinfo.naklist); free(group_list[listidx].fileinfo.section_done); if (group_list[listidx].restartinfo) { free(group_list[listidx].restartinfo->naklist); free(group_list[listidx].restartinfo->section_done); free(group_list[listidx].restartinfo); } memset(&group_list[listidx], 0, sizeof(group_list[listidx])); } else { // Don't clear the file_id in case we need to respond to late DONEs group_list[listidx].phase = PHASE_MIDGROUP; set_timeout(listidx); free(group_list[listidx].fileinfo.naklist); free(group_list[listidx].fileinfo.section_done); group_list[listidx].fileinfo.naklist = NULL; group_list[listidx].fileinfo.section_done = NULL; } }
/** * Clean up a group list entry. Close the file if open, * free malloc'ed structures, drop the multicast group * (if no one else is using it) and free the slot. */ void file_cleanup(struct group_list_t *group, int abort_session) { if (group->fileinfo.fd >= 0) { glog2(group, "starting file close"); close(group->fileinfo.fd); glog2(group, "done file close"); group->fileinfo.fd = -1; if (abort_session && !strcmp(tempdir, "")) { if (tempfile) { unlink(group->fileinfo.temppath); } else { unlink(group->fileinfo.filepath); } } else { if (tempfile) { move_to_backup(group); if (rename(group->fileinfo.temppath, group->fileinfo.filepath) == -1) { gsyserror(group, "Couldn't rename from %s to %s", group->fileinfo.temppath,group->fileinfo.filepath); } } if (group->fileinfo.tstamp) { utim_buf utbuf; utbuf.actime = group->fileinfo.tstamp; utbuf.modtime = group->fileinfo.tstamp; if (utime(group->fileinfo.filepath, &utbuf) == -1) { gsyserror(group, "utime failed"); } } } } if (abort_session || (group->file_id == 0)) { if (!addr_blank(&group->multi) && !other_mcast_users(group) && group->multi_join) { if (server_count > 0) { multicast_leave(listener, group->group_id, &group->multi, m_interface, interface_count, server_keys,server_count); if (has_proxy) { multicast_leave(listener, group->group_id, &group->multi, m_interface, interface_count, &proxy_info, 1); } } else { multicast_leave(listener, group->group_id, &group->multi, m_interface, interface_count, NULL, 0); } } if (group->server_pubkey.key) { if (group->keyextype == KEYEX_ECDH_ECDSA) { free_EC_key(group->server_pubkey.ec); } else { free_RSA_key(group->server_pubkey.rsa); } } if (group->server_dhkey.key) { free_EC_key(group->server_dhkey.ec); free_EC_key(group->client_dhkey.ec); } if (group->restartinfo && (strcmp(group->restartinfo->name, ""))) { // We have unused restart info from the last run. // Chalk this up as a loss and delete the data file char filepath[MAXPATHNAME]; snprintf(filepath, sizeof(filepath), "%s%c_group_%08X%c%s", tempdir, PATH_SEP, group->group_id, PATH_SEP, group->restartinfo->name); unlink(filepath); } if (abort_session) { write_restart_file(group); } free(group->loss_history); free(group->fileinfo.naklist); free(group->fileinfo.section_done); free(group->fileinfo.cache); free(group->fileinfo.cache_status); if (group->restartinfo) { free(group->restartinfo->naklist); free(group->restartinfo->section_done); free(group->restartinfo); } memset(group, 0, sizeof(struct group_list_t)); } else { // Don't clear the file_id in case we need to respond to late DONEs if (!strcmp(tempdir, "")) { run_postreceive(group, group->fileinfo.filepath); } group->phase = PHASE_MIDGROUP; set_timeout(group, 0); free(group->fileinfo.naklist); free(group->fileinfo.section_done); free(group->fileinfo.cache); free(group->fileinfo.cache_status); group->fileinfo.naklist = NULL; group->fileinfo.section_done = NULL; group->fileinfo.cache = NULL; group->fileinfo.cache_status = NULL; } }