/** * 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!"); } }
/** * For the current file in a group, move the existing file to * the appropriate backup directory, if it exists. * In the event of a failure, delete the original file */ void move_to_backup(int listidx) { stat_struct statbuf; char backup_file[MAXBACKUPPATHNAME], *trim_name; int len; if (lstat_func(group_list[listidx].fileinfo.filepath, &statbuf) == -1) { return; } if (backupcnt == 0) { clear_path(group_list[listidx].fileinfo.filepath, listidx); return; } #ifdef WINDOWS if ((group_list[listidx].fileinfo.filepath[1] == ':') && (group_list[listidx].fileinfo.filepath[2] == '\\')) { trim_name = &group_list[listidx].fileinfo.filepath[3]; } else { trim_name = group_list[listidx].fileinfo.filepath; } #else trim_name = group_list[listidx].fileinfo.filepath; #endif len = snprintf(backup_file, sizeof(backup_file), "%s%c%s%c%s%c%s", backupdir[group_list[listidx].fileinfo.destdiridx], PATH_SEP, group_list[listidx].start_date, PATH_SEP, group_list[listidx].start_time, PATH_SEP, trim_name); if (len >= sizeof(backup_file)) { log0(group_list[listidx].group_id, group_list[listidx].file_id, "Max pathname length exceeded for backup file, deleting", group_list[listidx].fileinfo.filepath); clear_path(group_list[listidx].fileinfo.filepath, listidx); return; } clear_path(backup_file, listidx); if (!create_path_to_file(listidx, backup_file)) { log0(group_list[listidx].group_id, group_list[listidx].file_id, "Error creating path to backup file"); clear_path(group_list[listidx].fileinfo.filepath, listidx); } #ifdef WINDOWS if (!MoveFile(group_list[listidx].fileinfo.filepath, backup_file)) { char errbuf[300]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, errbuf, sizeof(errbuf), NULL); log0(group_list[listidx].group_id, group_list[listidx].file_id, "Couldn't rename from %s to %s, deleting: (%d): %s", group_list[listidx].fileinfo.filepath, backup_file, GetLastError(), errbuf); clear_path(group_list[listidx].fileinfo.filepath, listidx); } else { log1(group_list[listidx].group_id, group_list[listidx].file_id, "Backed up existing file to %s", backup_file); } #else if (rename(group_list[listidx].fileinfo.filepath, backup_file) == -1) { syserror(group_list[listidx].group_id, group_list[listidx].file_id, "Couldn't rename from %s to %s, deleting", group_list[listidx].fileinfo.filepath, backup_file); clear_path(group_list[listidx].fileinfo.filepath, listidx); } else { log1(group_list[listidx].group_id, group_list[listidx].file_id, "Backed up existing file to %s", backup_file); } #endif }
/** * Creates all directories in the given file's path, removing existing files. * Returns 1 on success, 0 on failure */ int create_path_to_file(int listidx, const char *filename) { char *dir, *base; stat_struct statbuf; int rval; split_path(filename, &dir, &base); if (!dir) { log1(group_list[listidx].group_id, group_list[listidx].file_id, "Invalid path element %s", filename); rval = 0; goto end; } #ifdef WINDOWS if ((base == NULL) || ((strlen(dir) == 2) && (dir[1] == ':'))) { #else if ((!strcmp(dir, ".")) || (!strcmp(dir, "/"))) { #endif // At top level directory, so stop recursion rval = 1; goto end; } if (lstat_func(dir, &statbuf) != -1) { if (!S_ISDIR(statbuf.st_mode)) { if (unlink(dir) == -1) { syserror(group_list[listidx].group_id, group_list[listidx].file_id, "Failed to delete path element %s", dir); rval = 0; goto end; } if (mkdir(dir, 0755) == -1) { syserror(group_list[listidx].group_id, group_list[listidx].file_id, "Failed to create path element %s", dir); rval = 0; goto end; } } } else { // If the file's directory does not exist, recurse first to make sure // all parent directories exist if (!create_path_to_file(listidx, dir)) { rval = 0; goto end; } if (mkdir(dir, 0755) == -1) { syserror(group_list[listidx].group_id, group_list[listidx].file_id, "Failed to create path element %s", dir); rval = 0; goto end; } } rval = 1; end: free(dir); free(base); return rval; }
/** * Creates all directories in the given file's path, removing existing files. * Returns 1 on success, 0 on failure */ int create_path_to_file(struct group_list_t *group, const char *filename) { char *dir, *base; stat_struct statbuf; int rval; split_path(filename, &dir, &base); if (!dir) { glog1(group, "Invalid path element %s", filename); rval = 0; goto end; } #ifdef WINDOWS if ((base == NULL) || ((strlen(dir) == 2) && (dir[1] == ':'))) { #else if ((!strcmp(dir, ".")) || (!strcmp(dir, "/"))) { #endif // At top level directory, so stop recursion rval = 1; goto end; } if (lstat_func(dir, &statbuf) != -1) { if (!S_ISDIR(statbuf.st_mode)) { if (unlink(dir) == -1) { gsyserror(group, "Failed to delete path element %s", dir); rval = 0; goto end; } if (mkdir(dir, 0755) == -1) { gsyserror(group, "Failed to create path element %s", dir); rval = 0; goto end; } } } else { // If the file's directory does not exist, recurse first to make sure // all parent directories exist if (!create_path_to_file(group, dir)) { rval = 0; goto end; } if (mkdir(dir, 0755) == -1) { gsyserror(group, "Failed to create path element %s", dir); rval = 0; goto end; } } rval = 1; end: free(dir); free(base); return rval; } void new_loss_event(struct group_list_t *group, uint16_t txseq) { uint32_t seq_long; uint16_t count; int bytes, avgbytes, rate, grtt_usec; glog4(group, "Seq %d starts new loss event", txseq); // Found a new loss event if (txseq < group->max_txseq - MAXMISORDER) { glog5(group, "wrap check, i=%u, maxseq=%u", txseq, group->max_txseq); seq_long = ((group->seq_wrap - 1) << 16) | txseq; } else { seq_long = (group->seq_wrap << 16) | txseq; } if (group->slowstart) { group->slowstart = 0; // Initialize loss history count = group->max_txseq; bytes = 0; grtt_usec = (int)(group->grtt * 1000000); while ((count != group->start_txseq) && (diff_usec(group->loss_history[txseq].t, group->loss_history[count].t) < grtt_usec)) { bytes += group->loss_history[count--].size; } rate = (int)(bytes / group->grtt); glog4(group, "End slowstart, calculated rate = %d", rate); avgbytes= bytes / ((int16_t)(group->max_txseq - count)); group->loss_events[0].len = (int)(0 + pow( (rate * ((group->rtt != 0) ? group->rtt : group->grtt)) / (sqrt(1.5) * 8 * avgbytes), 2)); glog4(group, "Calculated prior event len = %d (rtt=%f, avgbytes=%d)", group->loss_events[0].len, group->rtt,avgbytes); } else { group->loss_events[0].len = seq_long - group->loss_events[0].start_seq; glog4(group, "Prior event length = %d (i=%u, start=%u)", group->loss_events[0].len, seq_long, group->loss_events[0].start_seq); } memmove(&group->loss_events[1], &group->loss_events[0], sizeof(struct loss_event_t) * 8); group->loss_events[0].start_seq = seq_long; group->loss_events[0].len = 0; group->loss_events[0].t = group->loss_history[txseq].t; }