コード例 #1
0
ファイル: client_transfer.c プロジェクト: No9/uftp
/**
 * 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);
    }
}
コード例 #2
0
ファイル: client_fileinfo.c プロジェクト: PumpkinSpace/uftp
/**
 * 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;
}
コード例 #3
0
ファイル: client_fileinfo.c プロジェクト: PumpkinSpace/uftp
/**
 * Perform FILEINFO processing specific to a regular file in restart mode
 * Returns 1 if a COMPLETE was sent in response, 0 otherwise
 */
int handle_fileinfo_restart(struct group_list_t *group)
{
    stat_struct statbuf;

    if ((!strcmp(group->fileinfo.name, group->restartinfo->name)) &&
            (group->fileinfo.size == group->restartinfo->size) &&
            (group->fileinfo.blocks == group->restartinfo->blocks) &&
            (group->fileinfo.sections == group->restartinfo->sections)) {
        // Flag this file to restart a failed transfer
        group->fileinfo.restart = 1;
        return 0;
    } else if ((lstat_func(group->fileinfo.filepath, &statbuf) != -1) &&
               S_ISREG(statbuf.st_mode) &&
               (statbuf.st_size == group->fileinfo.size)) {
        // This file was finished on the last attempt,
        // so respond with a COMPLETE right away
        early_complete(group, COMP_STAT_NORMAL, 0);
        return 1;
    }
    return 0;
}
コード例 #4
0
ファイル: client_fileinfo.c プロジェクト: PumpkinSpace/uftp
/**
 * 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!");
    }
}
コード例 #5
0
ファイル: client_common.c プロジェクト: b-cuts/uftp
/**
 * 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;
}
コード例 #6
0
ファイル: client_common.c プロジェクト: b-cuts/uftp
/**
 * 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
}
コード例 #7
0
ファイル: client_common.c プロジェクト: b-cuts/uftp
/**
 * Removes a full path from disk
 */
void clear_path(const char *path, int listidx)
{
    stat_struct statbuf;
    char filename[MAXPATHNAME];
    int len;

    if (lstat_func(path, &statbuf) == -1) {
        if (errno != ENOENT) {
            syserror(group_list[listidx].group_id, group_list[listidx].file_id,
                     "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_list[listidx].group_id, PATH_SEP);
        if ((ffhandle = _findfirsti64(dirglob, &finfo)) == -1) {
            syserror(group_list[listidx].group_id, group_list[listidx].file_id,
                     "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)) {
                log0(group_list[listidx].group_id, group_list[listidx].file_id,
                        "Max pathname length exceeded: %s%c%s",
                        filename, PATH_SEP, finfo.name);
                continue;
            }
            if (strcmp(finfo.name, ".") && strcmp(finfo.name, "..")) {
                clear_path(filename, listidx);
            }
        } while (_findnexti64(ffhandle, &finfo) == 0);
        _findclose(ffhandle);
#else
        DIR *dir;
        struct dirent *de;

        if ((dir = opendir(path)) == NULL) {
            syserror(group_list[listidx].group_id, group_list[listidx].file_id,
                     "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)) {
                log0(group_list[listidx].group_id, group_list[listidx].file_id,
                        "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, listidx);
            }
        }
        if (errno && (errno != ENOENT)) {
            syserror(group_list[listidx].group_id, group_list[listidx].file_id,
                     "Failed to read directory %s", path);
        }
        closedir(dir);
#endif
        if (rmdir(path) == -1) {
            syserror(group_list[listidx].group_id, group_list[listidx].file_id,
                     "Failed remove directory %s", path);
        }
    }
}
コード例 #8
0
ファイル: server_send.c プロジェクト: shoki/uftp
/**
 * Performs the send for a particular file/directory.  If a directory is
 * specified, get the list of files and call recursively for each.
 * Returns 0 if a file was sent and none received it, 1 otherwise
 */
int send_file(const char *basedir, const char *filename,
              const char *n_destfname, uint32_t group_id)
{
    static uint16_t file_id = 1;
    struct finfo_t finfo;
    stat_struct statbuf;
    char path[MAXPATHNAME], destpath[MAXPATHNAME];
    int len, rval, fd, emptydir;

    log(0, 0, "----- %s -----", filename);
    len = snprintf(path, sizeof(path), "%s%c%s", basedir, PATH_SEP, filename);
    if ((len >= sizeof(path)) || (len == -1)) {
        log(0, 0, "Max pathname length exceeded: %s%c%s",
                   basedir, PATH_SEP, filename);
        return 1;
    }
    if (follow_links) {
        rval = stat_func(path, &statbuf);
    } else {
        rval = lstat_func(path, &statbuf);
    }
    if (rval == -1) {
        syserror(0, 0, "Error getting file status for %s", filename);
        return 1;
    }
    if (file_excluded(filename)) {
        log(0, 0, "Skipping %s", filename);
        return 1;
    }
    rval = 1;
    if (S_ISREG(statbuf.st_mode)) {
        if ((fd = open(path, OPENREAD, 0)) == -1) {
            syserror(0, 0, "Error reading file %s", filename);
            return 1;
        }
        close(fd);
        memset(&finfo, 0, sizeof(struct finfo_t));
        finfo.ftype = FTYPE_REG;
        finfo.basedir = basedir;
        finfo.filename = filename;
        finfo.destfname = n_destfname;
        finfo.group_id = group_id;
        finfo.file_id = file_id++;
        if (file_id == 0) {
            file_id = 1;
        }
        finfo.size = statbuf.st_size;
        finfo.blocks = (int32_t)((finfo.size / blocksize) +
                (finfo.size % blocksize ? 1 :0));
        finfo.sections = (finfo.blocks / (blocksize * 8)) +
                (finfo.blocks % (blocksize * 8) ? 1 : 0);
        finfo.naklist = calloc(finfo.blocks, 1);
        finfo.deststate = calloc(destcount ? destcount : MAXDEST,
                sizeof(struct deststate_t));
        if ((finfo.naklist == NULL) || (finfo.deststate == NULL)) {
            syserror(0, 0, "calloc failed!");
            exit(1);
        }
        finfo.partial = 1;
        rval = announce_phase(&finfo);
        if (rval) {
            rval = transfer_phase(&finfo);
        }
        free(finfo.deststate);
        free(finfo.naklist);
#ifndef WINDOWS
    } else if (S_ISLNK(statbuf.st_mode)) {
        char linkname[MAXPATHNAME];

        memset(linkname, 0, sizeof(linkname));
        if (readlink(path, linkname, sizeof(linkname)-1) == -1) {
            syserror(0, 0, "Failed to read symbolic link %s", path);
            return 1;
        }
        // Both the file name and the link have to fit into a fileinfo_h.name
        if (strlen(linkname) + strlen(filename) + 2 > MAXPATHNAME) {
            log(0, 0, "Combined file name %s and link %s too long",
                      filename, linkname);
            return 1;
        }
        memset(&finfo, 0, sizeof(struct finfo_t));
        finfo.ftype = FTYPE_LINK;
        finfo.basedir = basedir;
        finfo.filename = filename;
        finfo.destfname = n_destfname;
        finfo.linkname = linkname;
        finfo.group_id = group_id;
        finfo.file_id = file_id++;
        if (file_id == 0) {
            file_id = 1;
        }
        finfo.deststate = calloc(destcount ? destcount : MAXDEST,
                sizeof(struct deststate_t));
        if (finfo.deststate == NULL) {
            syserror(0, 0, "calloc failed!");
            exit(1);
        }
        finfo.partial = 1;
        rval = announce_phase(&finfo);
        if (rval) {
            rval = transfer_phase(&finfo);
        }
#endif
    } else if (S_ISDIR(statbuf.st_mode)) {
        // read directory and do recursive send
#ifdef WINDOWS
        intptr_t ffhandle;
        struct _finddatai64_t ffinfo;
        char dirglob[MAXPATHNAME];

        snprintf(dirglob, sizeof(dirglob), "%s%c%s%c*", basedir, PATH_SEP,
                                                        filename, PATH_SEP);
        if ((ffhandle = _findfirsti64(dirglob, &ffinfo)) == -1) {
            syserror(0, 0, "Failed to open directory %s%c%s", basedir, PATH_SEP,
                                                              filename);
            return 1;
        }
        emptydir = 1;
        do {
            len = snprintf(path, sizeof(path), "%s/%s", filename, ffinfo.name);
            if ((len >= sizeof(path)) || (len == -1)) {
                log(0, 0, "Max pathname length exceeded: %s/%s",
                           filename, ffinfo.name);
                continue;
            }
            len = snprintf(destpath, sizeof(destpath), "%s/%s",
                           n_destfname, ffinfo.name);
            if ((len >= sizeof(destpath)) || (len == -1)) {
                log(0, 0, "Max pathname length exceeded: %s/%s",
                           n_destfname, ffinfo.name);
                continue;
            }
            if (strcmp(ffinfo.name, ".") && strcmp(ffinfo.name, "..")) {
                emptydir = 0;
                if (!send_file(basedir, path, destpath, group_id)) {
                    rval = 0;
                    break;
                }
            }
        } while (_findnexti64(ffhandle, &ffinfo) == 0);
        _findclose(ffhandle);
#else
        DIR *dir;
        struct dirent *de;
        char dirname[MAXPATHNAME];

        snprintf(dirname, sizeof(dirname), "%s%c%s", basedir,PATH_SEP,filename);
        if ((dir = opendir(dirname)) == NULL) {
            syserror(0, 0, "Failed to open directory %s", dirname);
            return 1;
        }
        // errno needs to be set to 0 before calling readdir, otherwise
        // we'll report a false error when we exhaust the directory
        emptydir = 1;
        while ((errno = 0, de = readdir(dir)) != NULL) {
            len = snprintf(path, sizeof(path), "%s/%s", filename, de->d_name);
            if ((len >= sizeof(path)) || (len == -1)) {
                log(0, 0, "Max pathname length exceeded: %s/%s",
                           filename, de->d_name);
                continue;
            }
            len = snprintf(destpath, sizeof(destpath), "%s/%s",
                           n_destfname, de->d_name);
            if ((len >= sizeof(destpath)) || (len == -1)) {
                log(0, 0, "Max pathname length exceeded: %s/%s",
                           n_destfname, de->d_name);
                continue;
            }
            if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
                emptydir = 0;
                if (!send_file(basedir, path, destpath, group_id)) {
                    rval = 0;
                    break;
                }
            }
        }
        if (errno && (errno != ENOENT)) {
            syserror(0, 0, "Failed to read directory %s", filename);
        }
        closedir(dir);
#endif
        if (emptydir) {
            memset(&finfo, 0, sizeof(struct finfo_t));
            finfo.ftype = FTYPE_DIR;
            finfo.basedir = basedir;
            finfo.filename = filename;
            finfo.destfname = n_destfname;
            finfo.group_id = group_id;
            finfo.file_id = file_id++;
            if (file_id == 0) {
                file_id = 1;
            }
            finfo.deststate = calloc(destcount ? destcount : MAXDEST,
                    sizeof(struct deststate_t));
            if (finfo.deststate == NULL) {
                syserror(0, 0, "calloc failed!");
                exit(1);
            }
            finfo.partial = 1;
            rval = announce_phase(&finfo);
            if (rval) {
                rval = transfer_phase(&finfo);
            }
        }
    } else {
        log(0, 0, "Skipping special file %s", filename);
    }
    return rval;
}
コード例 #9
0
ファイル: client_common.c プロジェクト: No9/uftp
/**
 * 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;
}