/** * Moves a file from the temp to the destination directory */ void move_file_individual(struct group_list_t *group, const char *local_temp, const char *local_dest, const char *filename) { char temppath[MAXPATHNAME], destpath[MAXPATHNAME]; stat_struct temp_stat, dest_stat; int len, found_dir; len = snprintf(temppath, sizeof(temppath), "%s%c%s", local_temp, PATH_SEP, filename); if ((len >= sizeof(temppath)) || (len == -1)) { glog0(group, "Max pathname length exceeded: %s%c%s", local_temp, PATH_SEP, filename); return; } len = snprintf(destpath, sizeof(destpath), "%s%c%s", local_dest, PATH_SEP, filename); if ((len >= sizeof(destpath)) || (len == -1)) { glog0(group, "Max pathname length exceeded: %s%c%s", local_dest, PATH_SEP, filename); return; } if (lstat_func(temppath, &temp_stat) == -1) { gsyserror(group, "Error getting file status for %s", temppath); return; } if (S_ISDIR(temp_stat.st_mode)) { found_dir = 0; if (lstat_func(destpath, &dest_stat) != -1) { if (!S_ISDIR(dest_stat.st_mode)) { clear_path(destpath, group); } else { found_dir = 1; } } if (!found_dir) { if (mkdir(destpath, 0755) == -1) { gsyserror(group, "Failed to create directory %s", destpath); return; } } move_files_individual(group, temppath, destpath); } else { clear_path(destpath, group); #ifdef WINDOWS if (!MoveFile(temppath, destpath)) { char errbuf[300]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, errbuf, sizeof(errbuf),NULL); glog0(group, "error (%d): %s", GetLastError(), errbuf); } #else if (rename(temppath, destpath) == -1) { gsyserror(group, "Couldn't move file"); } #endif run_postreceive(group, destpath); } }
/** * Flushes the cache to disk * Returns 1 on success, 0 on failure */ int flush_disk_cache(struct group_list_t *group) { f_offset_t offset, seek_rval; int wrote_len; uint32_t i; if (group->fileinfo.cache_len == 0) return 1; offset = (f_offset_t) group->fileinfo.cache_start * group->blocksize; if ((seek_rval = lseek_func(group->fileinfo.fd, offset - group->fileinfo.curr_offset, SEEK_CUR)) == -1) { gsyserror(group, "lseek failed for file"); } if (seek_rval != offset) { glog2(group, "offset is %s", printll(seek_rval)); glog2(group, " should be %s", printll(offset)); if ((seek_rval = lseek_func(group->fileinfo.fd, offset, SEEK_SET)) == -1) { gsyserror(group, "lseek failed for file"); return 0; } } if ((wrote_len = write(group->fileinfo.fd, group->fileinfo.cache, group->fileinfo.cache_len)) == -1) { gsyserror(group, "Write failed for blocks %d - %d", group->fileinfo.cache_start, group->fileinfo.cache_end); return 0; } else { group->fileinfo.curr_offset = offset + wrote_len; if (wrote_len != group->fileinfo.cache_len) { glog0(group, "Write failed for blocks %d - %d, only wrote %d bytes", group->fileinfo.cache_start, group->fileinfo.cache_end); return 0; } else { glog4(group, "Wrote blocks %d - %d to disk from cache", group->fileinfo.cache_start, group->fileinfo.cache_end); for (i = group->fileinfo.cache_start; i <= group->fileinfo.cache_end; i++) { int status_idx = i - group->fileinfo.cache_start; if (group->fileinfo.cache_status[status_idx]) { group->fileinfo.naklist[i] = 0; } } group->fileinfo.cache_start = group->fileinfo.cache_end + 1; while ((group->fileinfo.cache_start < group->fileinfo.blocks) && (!group->fileinfo.naklist[group->fileinfo.cache_start])) { group->fileinfo.cache_start++; } group->fileinfo.cache_end = group->fileinfo.cache_start; group->fileinfo.cache_len = 0; memset(group->fileinfo.cache, 0, cache_len); memset(group->fileinfo.cache_status,0,cache_len / group->blocksize); return 1; } } }
/** * Move all files from temp to destination directory if at end of group. * Called recursively to move each file individually. */ void move_files_individual(struct group_list_t *group, const char *local_temp, char *local_dest) { int emptydir; { #ifdef WINDOWS intptr_t ffhandle; struct _finddatai64_t finfo; char dirglob[MAXPATHNAME]; snprintf(dirglob, sizeof(dirglob), "%s%c*", local_temp, PATH_SEP); if ((ffhandle = _findfirsti64(dirglob, &finfo)) == -1) { gsyserror(group, "Failed to open directory %s", dirglob); return; } emptydir = 1; do { if (strcmp(finfo.name, ".") && strcmp(finfo.name, "..")) { emptydir = 0; move_file_individual(group, local_temp, local_dest, finfo.name); } } while (_findnexti64(ffhandle, &finfo) == 0); _findclose(ffhandle); #else DIR *dir; struct dirent *de; if ((dir = opendir(local_temp)) == NULL) { gsyserror(group, "Failed to open directory %s", local_temp); return; } emptydir = 1; // errno needs to be set to 0 before calling readdir, otherwise // we'll report a false error when we exhaust the directory while ((errno = 0, de = readdir(dir)) != NULL) { if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) { emptydir = 0; move_file_individual(group, local_temp, local_dest, de->d_name); } } if (errno && (errno != ENOENT)) { gsyserror(group, "Failed to read directory %s", tempdir); } closedir(dir); #endif } if (emptydir) { run_postreceive(group, local_dest); } if (rmdir(local_temp) == -1) { gsyserror(group, "Failed remove temp directory %s", local_temp); } }
/** * Save the state of a failed transfer so it can restarted later. */ void write_restart_file(struct group_list_t *group) { struct file_t *fileinfo; struct client_restart_t restart; char restart_name[MAXPATHNAME]; int fd; // Don't bother if we're not using a temp directory. if (!strcmp(tempdir, "")) { return; } glog2(group, "Writing restart file"); memset(&restart, 0, sizeof(restart)); fileinfo = &group->fileinfo; if (group->phase != PHASE_MIDGROUP) { restart.blocks = fileinfo->blocks; restart.sections = fileinfo->sections; restart.size = fileinfo->size; strncpy(restart.name, fileinfo->name, sizeof(restart.name)); restart.name[sizeof(restart.name)-1] = '\x0'; } snprintf(restart_name, sizeof(restart_name), "%s%c_group_%08X_restart", tempdir, PATH_SEP, group->group_id); if ((fd = open(restart_name, OPENWRITE | O_CREAT | O_TRUNC, 0644)) == -1) { gsyserror(group, "Failed to create restart file"); return; } if (file_write(fd, &restart, sizeof(restart)) == -1) { glog0(group, "Failed to write header for restart file"); goto errexit; } if (fileinfo->blocks && fileinfo->naklist) { if (file_write(fd, fileinfo->naklist, fileinfo->blocks) == -1) { glog0(group, "Failed to write NAK list for restart file"); goto errexit; } } if (fileinfo->sections && fileinfo->section_done) { if (file_write(fd, fileinfo->section_done, fileinfo->sections) == -1) { glog0(group, "Failed to write section_done list for restart file"); goto errexit; } } close(fd); return; errexit: close(fd); unlink(restart_name); }
/** * Perform FILEINFO processing specific to an empty directory */ void handle_fileinfo_dir(struct group_list_t *group, int found_dir) { if (!found_dir && !group->sync_preview) { glog2(group, "Creating directory"); if (mkdir(group->fileinfo.filepath, 0755) == -1) { gsyserror(group, "Failed to create directory %s", group->fileinfo.filepath); early_complete(group, COMP_STAT_REJECTED, 0); return; } } early_complete(group, found_dir ? COMP_STAT_SKIPPED : COMP_STAT_NORMAL, 0); }
/** * Perform FILEINFO processing specific to a symbolic link */ void handle_fileinfo_link(struct group_list_t *group) { #ifndef WINDOWS if (!group->sync_preview) { if (symlink(group->fileinfo.linkname, group->fileinfo.filepath) == -1) { gsyserror(group, "Failed to create symlink %s", group->fileinfo.filepath); early_complete(group, COMP_STAT_REJECTED, 0); return; } } #endif 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!"); } }
/** * Perform FILEINFO processing specific to a regular file */ void handle_fileinfo_regular(struct group_list_t *group) { // First handle restart or sync mode, // then create/open the file. if (group->restartinfo) { if (handle_fileinfo_restart(group)) { return; } } else if (group->sync_mode) { if (handle_fileinfo_sync(group)) { return; } } if (group->fileinfo.restart) { group->fileinfo.fd = open(group->fileinfo.filepath, OPENWRITE); } else { const char *filename; if (tempfile) { filename = group->fileinfo.temppath; } else { filename = group->fileinfo.filepath; } #ifdef WINDOWS SetFileAttributes(filename, FILE_ATTRIBUTE_NORMAL); #else chmod(filename, 0644); #endif group->fileinfo.fd = open(filename, OPENWRITE | O_CREAT | O_TRUNC,0644); } if (group->fileinfo.fd == -1) { gsyserror(group, "Error opening data file"); early_complete(group, COMP_STAT_REJECTED, 0); return; } if (group->fileinfo.size > free_space(group->fileinfo.filepath)) { glog0(group, "Not enough disk space, aborting"); send_abort(group, "Not enough disk space"); return; } // Final preparations for receiving a file. if (group->fileinfo.restart) { group->fileinfo.naklist = group->restartinfo->naklist; group->fileinfo.section_done = group->restartinfo->section_done; group->restartinfo->naklist = NULL; group->restartinfo->section_done = NULL; free(group->restartinfo); group->restartinfo = NULL; } else { group->fileinfo.naklist = safe_calloc(group->fileinfo.blocks, 1); group->fileinfo.section_done = safe_calloc(group->fileinfo.sections, 1); memset(group->fileinfo.naklist, 1, group->fileinfo.blocks); } group->fileinfo.last_block = -1; group->fileinfo.last_section = 0; group->fileinfo.curr_offset = 0; group->fileinfo.cache_start = 0; group->fileinfo.cache_end = 0; group->fileinfo.cache_len = 0; group->fileinfo.cache = safe_calloc(cache_len, 1); group->fileinfo.cache_status = safe_calloc(cache_len / group->blocksize, 1); group->phase = PHASE_RECEIVING; send_fileinfo_ack(group, group->fileinfo.restart); set_timeout(group, 0); }
/** * Move all files from temp to destination directory if at end of group */ void move_files(struct group_list_t *group) { char temppath[MAXPATHNAME], destpath[MAXPATHNAME]; char *filelist[10000]; // TODO: no magic number int len, filecount, i; if (!strcmp(tempdir, "") || (group->file_id != 0)) { return; } if (move_individual) { len = snprintf(temppath, sizeof(temppath), "%s%c_group_%08X", tempdir, PATH_SEP, group->group_id); if ((len >= sizeof(temppath)) || (len == -1)) { glog0(group, "Max pathname length exceeded: %s%c_group_%08X", tempdir, PATH_SEP, group->group_id); } else { move_files_individual(group, temppath, destdir[0]); } return; } { #ifdef WINDOWS intptr_t ffhandle; struct _finddatai64_t finfo; char dirglob[MAXPATHNAME]; snprintf(dirglob, sizeof(dirglob), "%s%c_group_%08X%c*", tempdir, PATH_SEP, group->group_id, PATH_SEP); if ((ffhandle = _findfirsti64(dirglob, &finfo)) == -1) { gsyserror(group, "Failed to open directory %s", dirglob); return; } filecount = 0; do { len = snprintf(temppath, sizeof(temppath), "%s%c_group_%08X%c%s", tempdir, PATH_SEP, group->group_id, PATH_SEP, finfo.name); if ((len >= sizeof(temppath)) || (len == -1)) { glog0(group,"Max pathname length exceeded: %s%c_group_%08X%c%s", tempdir, PATH_SEP, group->group_id, PATH_SEP, finfo.name); continue; } len = snprintf(destpath, sizeof(destpath), "%s%c%s", destdir[0], PATH_SEP, finfo.name); if ((len >= sizeof(destpath)) || (len == -1)) { glog0(group, "Max pathname length exceeded: %s%c%s", destdir[0], PATH_SEP, finfo.name); continue; } // do the move if (strcmp(finfo.name, ".") && strcmp(finfo.name, "..")) { clear_path(destpath, group); if (!MoveFile(temppath, destpath)) { char errbuf[300]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, errbuf, sizeof(errbuf), NULL); glog0(group, "error (%d): %s", GetLastError(), errbuf); } filelist[filecount] = strdup(destpath); if (filelist[filecount] == NULL) { gsyserror(group, "strdup failed!"); exit(ERR_ALLOC); } filecount++; } } while (_findnexti64(ffhandle, &finfo) == 0); _findclose(ffhandle); #else DIR *dir; struct dirent *de; char dirname[MAXPATHNAME]; snprintf(dirname, sizeof(dirname), "%s%c_group_%08X", tempdir, PATH_SEP, group->group_id); if ((dir = opendir(dirname)) == NULL) { gsyserror(group, "Failed to open directory %s", dirname); return; } filecount = 0; // errno needs to be set to 0 before calling readdir, otherwise // we'll report a false error when we exhaust the directory while ((errno = 0, de = readdir(dir)) != NULL) { len = snprintf(temppath, sizeof(temppath), "%s%c%s", dirname, PATH_SEP, de->d_name); if ((len >= sizeof(temppath)) || (len == -1)) { glog0(group, "Max pathname length exceeded: %s%c%s", dirname, PATH_SEP, de->d_name); continue; } len = snprintf(destpath, sizeof(destpath), "%s%c%s", destdir[0], PATH_SEP, de->d_name); if ((len >= sizeof(destpath)) || (len == -1)) { glog0(group, "Max pathname length exceeded: %s%c%s", destdir[0], PATH_SEP, de->d_name); continue; } if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) { clear_path(destpath, group); if (rename(temppath, destpath) == -1) { gsyserror(group, "Couldn't move file"); } filelist[filecount] = strdup(destpath); if (filelist[filecount] == NULL) { gsyserror(group, "strdup failed!"); exit(ERR_ALLOC); } filecount++; } } if (errno && (errno != ENOENT)) { gsyserror(group, "Failed to read directory %s", dirname); } closedir(dir); #endif } run_postreceive_multi(group, filelist, filecount); for (i = 0; i < filecount; i++) { free(filelist[i]); } snprintf(temppath, sizeof(temppath), "%s%c_group_%08X", tempdir, PATH_SEP, group->group_id); if (rmdir(temppath) == -1) { gsyserror(group, "Failed remove temp directory %s", temppath); } }
/** * 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; }
/** * 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(struct group_list_t *group) { stat_struct statbuf; char backup_file[MAXBACKUPPATHNAME], *trim_name; int len; if (lstat_func(group->fileinfo.filepath, &statbuf) == -1) { return; } if (backupcnt == 0) { clear_path(group->fileinfo.filepath, group); return; } #ifdef WINDOWS if ((group->fileinfo.filepath[1] == ':') && (group->fileinfo.filepath[2] == '\\')) { trim_name = &group->fileinfo.filepath[3]; } else { trim_name = group->fileinfo.filepath; } #else trim_name = group->fileinfo.filepath; #endif len = snprintf(backup_file, sizeof(backup_file), "%s%c%s%c%s%c%s", backupdir[group->fileinfo.destdiridx], PATH_SEP, group->start_date, PATH_SEP, group->start_time, PATH_SEP, trim_name); if (len >= sizeof(backup_file)) { glog0(group, "Max pathname length exceeded for backup file, deleting", group->fileinfo.filepath); clear_path(group->fileinfo.filepath, group); return; } clear_path(backup_file, group); if (!create_path_to_file(group, backup_file)) { glog0(group, "Error creating path to backup file"); clear_path(group->fileinfo.filepath, group); } #ifdef WINDOWS if (!MoveFile(group->fileinfo.filepath, backup_file)) { char errbuf[300]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, errbuf, sizeof(errbuf), NULL); glog0(group, "Couldn't rename from %s to %s, deleting: (%d): %s", group->fileinfo.filepath, backup_file, GetLastError(), errbuf); clear_path(group->fileinfo.filepath, group); } else { glog2(group, "Backed up existing file to %s", backup_file); } #else if (rename(group->fileinfo.filepath, backup_file) == -1) { gsyserror(group, "Couldn't rename from %s to %s, deleting", group->fileinfo.filepath, backup_file); clear_path(group->fileinfo.filepath, group); } else { glog2(group, "Backed up existing file to %s", backup_file); } #endif }
/** * Removes a full path from disk */ void clear_path(const char *path, struct group_list_t *group) { stat_struct statbuf; char filename[MAXPATHNAME]; int len; if (lstat_func(path, &statbuf) == -1) { if (errno != ENOENT) { gsyserror(group, "Error getting file status for %s", path); } return; } if (!S_ISDIR(statbuf.st_mode)) { unlink(path); } else { #ifdef WINDOWS intptr_t ffhandle; struct _finddatai64_t finfo; char dirglob[MAXPATHNAME]; snprintf(dirglob, sizeof(dirglob), "%s%c*", path, PATH_SEP, group->group_id, PATH_SEP); if ((ffhandle = _findfirsti64(dirglob, &finfo)) == -1) { gsyserror(group, "Failed to open directory %s", path); return; } do { len = snprintf(filename, sizeof(filename), "%s%c%s", path, PATH_SEP, finfo.name); if ((len >= sizeof(filename)) || (len == -1)) { glog0(group, "Max pathname length exceeded: %s%c%s", filename, PATH_SEP, finfo.name); continue; } if (strcmp(finfo.name, ".") && strcmp(finfo.name, "..")) { clear_path(filename, group); } } while (_findnexti64(ffhandle, &finfo) == 0); _findclose(ffhandle); #else DIR *dir; struct dirent *de; if ((dir = opendir(path)) == NULL) { gsyserror(group, "Failed to open directory %s", path); return; } // errno needs to be set to 0 before calling readdir, otherwise // we'll report a false error when we exhaust the directory while ((errno = 0, de = readdir(dir)) != NULL) { len = snprintf(filename, sizeof(filename), "%s%c%s", path, PATH_SEP, de->d_name); if ((len >= sizeof(filename)) || (len == -1)) { glog0(group, "Max pathname length exceeded: %s%c%s", path, PATH_SEP, de->d_name); continue; } if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) { clear_path(filename, group); } } if (errno && (errno != ENOENT)) { gsyserror(group, "Failed to read directory %s", path); } closedir(dir); #endif if (rmdir(path) == -1) { gsyserror(group, "Failed remove directory %s", path); } } }
/** * 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; } }
/** * Run the postreceive script on list of received files */ void run_postreceive_multi(struct group_list_t *group, char *const *files, int count) { char **params; char gid_str[10]; char gid_param[] = "-I"; int i; if (!strcmp(postreceive, "")) { return; } params = safe_calloc(count + 4, sizeof(char *)); snprintf(gid_str, sizeof(gid_str), "%08X", group->group_id); params[0] = postreceive; params[1] = gid_param; params[2] = gid_str; for (i = 0; i < count; i++) { params[i+3] = files[i]; } params[count+4-1] = NULL; if (log_level >= 2) { cglog2(group, "Running postreceive: %s", postreceive); for (i = 1; i < count + 3; i++) { sclog2(" %s", params[i]); } slog2(""); } #ifdef WINDOWS { char cmdline[0x8000]; // Windows max command line length char cmdexe[MAXPATHNAME]; int too_long, rval, is_cmd; strcpy(cmdline, ""); if ((!strncmp(&postreceive[strlen(postreceive)-4], ".cmd", 4)) || (!strncmp(&postreceive[strlen(postreceive)-4], ".bat", 4))) { is_cmd = 1; if (!GetEnvironmentVariable("SystemRoot", cmdexe, sizeof(cmdexe))) { char errbuf[300]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, errbuf, sizeof(errbuf), NULL); glog0(group, "Error getting sysroot: (%d) %s", GetLastError(), errbuf); free(params); return; } strcat(cmdexe, "\\system32\\cmd.exe"); strcat(cmdline, "/c \""); } else { is_cmd = 0; } for (too_long = 0, i = 0; i < count + 3; i++) { int size = 0x8000 - strlen(cmdline); if (size <= (int)strlen(params[i]) + 4) { too_long = 1; break; } // Quote everything except -I {group_id} if (i == 1 || i == 2) { strcat(cmdline, params[i]); strcat(cmdline," "); } else { strcat(cmdline, "\""); strcat(cmdline, params[i]); strcat(cmdline,"\" "); } } if (is_cmd) { strcat(cmdline, "\""); } if (!too_long) { STARTUPINFO startup_info; PROCESS_INFORMATION proc_info; GetStartupInfo(&startup_info); rval = CreateProcess(is_cmd ? cmdexe : postreceive, cmdline, NULL, NULL, 0, CREATE_NO_WINDOW, NULL, NULL, &startup_info, &proc_info); if (!rval) { char errbuf[300]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, errbuf, sizeof(errbuf), NULL); glog0(group, "Error running script: (%d) %s", GetLastError(), errbuf); } } } #else { pid_t pid; if ((pid = fork()) == -1) { gsyserror(group, "fork failed"); } else if (pid == 0) { close(listener); close(1); close(2); execv(postreceive, params); gsyserror(group, "exec failed"); exit(1); } } #endif free(params); }
/** * Reads in the contents of the restart file. */ void read_restart_file(struct group_list_t *group) { struct client_restart_t *restart; char restart_name[MAXPATHNAME]; int fd, i, rval; // Don't bother if we're not using a temp directory. if (!strcmp(tempdir, "")) { return; } // First abort any prior session with the same group_id. // This creates the restart file. for (i = 0; i < MAXLIST; i++) { if ((group_list[i].group_id == group->group_id) && (group_list[i].group_inst < group->group_inst)) { file_cleanup(&group_list[i], 1); } } glog2(group, "Reading restart file"); snprintf(restart_name, sizeof(restart_name), "%s%c_group_%08X_restart", tempdir, PATH_SEP, group->group_id); if ((fd = open(restart_name, OPENREAD, 0644)) == -1) { gsyserror(group, "Failed to read restart file"); return; } // Read header restart = safe_calloc(sizeof(struct client_restart_t), 1); if ((rval = file_read(fd, restart, sizeof(struct client_restart_t), 0)) == -1) { glog0(group, "Failed to read header for restart file"); goto err1; } if (rval != sizeof(struct client_restart_t)) { glog0(group, "Failed to read header for restart file " "(read %d, expected %d)", rval,sizeof(struct client_restart_t)); goto err1; } // Read NAK list if (restart->blocks) { restart->naklist = safe_calloc(restart->blocks, 1); if (file_read(fd, restart->naklist, restart->blocks, 0) == -1) { glog0(group, "Failed to read NAK list for restart file"); goto err2; } } // Read section_done list if (restart->sections) { restart->section_done = safe_calloc(restart->sections, 1); if (file_read(fd, restart->section_done, restart->sections, 0) == -1) { glog0(group, "Failed to read section_done list for restart file"); goto err3; } } close(fd); unlink(restart_name); group->restartinfo = restart; glog3(group, "Reading restart file done"); return; err3: free(restart->section_done); err2: free(restart->naklist); err1: free(restart); close(fd); }