// -----------------------------------------------------------------------------
//  Read a <block> from <index>
//
//  We point out the difference of counting among the <number>, <act_block> 
//  and <pos>.
//  (1) <num_blocks_>: record the number of blocks, excluding the block
//      of header. start from 1. (internal block)
//  (2) <act_block_>: record the number of blocks currently read or written,
//      including the block of header, thus when we read or write file,
//      current <act_block> equal to 1. <act_block> is corresponding to
//      file pointer.
//  (3) <index> : record position of block we want to read or write, excluding
//      the block of header. start from 0. (external block), i.e., when 
//      <index> = 0, the file pointer is pointed to next block after the 
//      block of header, at this time, <act_block_> equals to 1.
//
//  i.e. if number = 3, there are 4 blocks in the file, 1 header block +
//  3 data block.
//
//  When file is opened, <act_block_> = 1. if <index> = 1, it means that we 
//  want to read or write the 3rd block (2nd data block), thus firstly index++,
//  then <index> = 2, then fseek move to 2nd data block.
//
//  After reading or writing the 2nd data block, file pointer is pointed to 
//  the 3rd data block. As we know it has read or written 3 blocks, thus 
//  currently <act_block> = <index> + 1 = 2 + 1 = 3.
// -----------------------------------------------------------------------------
bool BlockFile::read_block(			// read a <block> from <index>
	Block block,						// a <block> (return)
	int index)							// pos of the block
{
	index++;						// extrnl block to intrnl block
									// move to the position
	if (index <= num_blocks_ && index > 0) {
		seek_block(index);
	}
	else {
		printf("BlockFile::read_block request the block %d "
			"which is illegal.", index - 1);
		error("\n", true);
	}

	get_bytes(block, block_length_);// read the block
	if (index + 1 > num_blocks_) {	// <fp_> reaches the end of file
		fseek(fp_, 0, SEEK_SET);
		act_block_ = 0;				// <act_block_> rewind to start pos
	}
	else {
		act_block_ = index + 1;		// <act_block_> to next pos
	}
	return true;
}
// -----------------------------------------------------------------------------
//  Note that this function can only write to an already "allocated" block (in 
//  the range of <num_blocks>).
//  If you allocate a new block, please use "append_block" instead.
// -----------------------------------------------------------------------------
bool BlockFile::write_block(		// write a <block> into <index>
	Block block,						// a <block>
	int index)							// position of the blocks
{
	index++;						// extrnl block to intrnl block
									// move to the position
	if (index <= num_blocks_ && index > 0) {
		seek_block(index);
	}
	else {
		printf("BlockFile::write_block request the block %d "
			"which is illegal.", index - 1);
		error("\n", true);
	}

	put_bytes(block, block_length_);// write this block
	if (index + 1 > num_blocks_) {	// update <act_block_>
		fseek(fp_, 0, SEEK_SET);
		act_block_ = 0;
	}
	else {
		act_block_ = index + 1;
	}
	return true;
}
示例#3
0
/**
 * Performs the Transfer phase for a particular file
 * Returns 1 if at least one client finished, 0 if all are dropped or aborted
 */
int transfer_phase(struct finfo_t *finfo)
{
    unsigned char *packet, *encpacket, *data;
    char path[MAXPATHNAME];
    struct uftp_h *header;
    struct fileseg_h *fileseg;
    int max_time, alldone, numbytes, sent_blocks, current_naks;
    unsigned int pass, section, numnaks, block;
    struct timeval start_time, last_sent, current_sent;
    int64_t avgwait, waitcnt, overage, tdiff;
    f_offset_t offset, curr_offset;
    int file, i;

    if (finfo->file_id != 0) {
        // First check to see if all clients are already done for this file.
        // This can happen on a restart when the file finished on the
        // last attempt and responded to the FILEINFO with a COMPLETE
        for (i = 0, alldone = 1; (i < destcount) && alldone; i++) {
            alldone = alldone && ((destlist[i].status == DEST_DONE) ||
                        (client_error(i)) || (destlist[i].clientcnt != -1));
        }
        if (alldone) {
            gettimeofday(&start_time, NULL);
            print_status(finfo, start_time);
            return 1;
        }
    }

    // If rate is -1, use 100Mbps for purpose of calculating max time
    max_time = (int)floor(((double)weight / 100) *
           ((double)finfo->size / (((rate == -1) ? 100000 : rate) / 8) / 1024));
    if (max_time < min_time) {
        max_time = min_time;
    }

    if ((finfo->file_id != 0) && (finfo->ftype == FTYPE_REG)) {
        log(0, 0, "Maximum file transfer time: %d seconds", max_time);
        snprintf(path, sizeof(path), "%s%c%s", finfo->basedir, PATH_SEP,
                                               finfo->filename);
        if ((file = open(path, OPENREAD, 0)) == -1) {
            syserror(0, 0, "Error opening file");
            return 1;
        }
    } else {
        // At end of group, all non-errored client are DEST_DONE from the
        // last file, so reset them to DEST_ACTIVE to get the final COMPLETE.
        for (i = 0; i < destcount; i++) {
            if (!client_error(i)) {
                destlist[i].status = DEST_ACTIVE;
            }
        }
    }

    packet = calloc(mtu, 1);
    encpacket = calloc(mtu, 1);
    if ((packet == NULL) || (encpacket == NULL)) {
        syserror(0, 0, "calloc failed!");
        exit(1);
    }
    header = (struct uftp_h *)packet;
    fileseg = (struct fileseg_h *)(packet + sizeof(struct uftp_h));
    data = (unsigned char *)fileseg + sizeof(struct fileseg_h);
    set_uftp_header(header, FILESEG, finfo, &receive_dest);    

    pass = 1;
    alldone = 0;
    gettimeofday(&start_time, NULL);
    do {
        avgwait = 0;
        waitcnt = 0;
        numnaks = 0;
        overage = 0;
        section = 1;
        curr_offset = 0;
        sent_blocks = 0;

        gettimeofday(&last_sent, NULL);
        if (finfo->file_id != 0) {
            log(0, 0, "Sending file...pass %d", pass);
            lseek_func(file, 0, SEEK_SET);
        } else {
            log(0, 0, "Finishing group");
        }
        fileseg->func = FILESEG;
        fileseg->file_id = htons(finfo->file_id);
        fileseg->pass = pass;
        fileseg->section = htons(section);
        for (block = 0; block < finfo->blocks; block++) {
            // If all clients received this file partially on a prior attempt
            // and it's the first pass, request NAKs for all sections
            // right away and don't send any data packets.
            if (((pass == 1) || finfo->naklist[block]) &&
                    !((pass == 1) && finfo->partial)) {
                if (diff_sec(last_sent, start_time) > max_time) {
                    log0(0, 0, "Max file transfer time exceeded");
                    send_abort(finfo, "Max file transfer time exceeded",
                            &receive_dest, NULL, (keytype != KEY_NONE),
                            !quit_on_error);
                    alldone = 1;
                    for (i = 0; i < destcount; i++) {
                        if (quit_on_error || ((destlist[i].status == DEST_ACTIVE) &&
                                               destlist[i].clientcnt == -1 )) {
                            destlist[i].status = DEST_ABORT;
                        }
                    }
                    break;
                }
                // On the first pass, go straight through the file.
                // On later passes, seek to the next packet.
                if (pass != 1) {
                    log4(0, 0, "Resending %d", block);
                    if (!seek_block(file, block, &offset, curr_offset)) {
                        continue;
                    }
                }
                if ((numbytes = read(file, data, blocksize)) == -1) {
                    syserror(0, 0, "read failed");
                    continue;
                }
                if (pass != 1) {
                    curr_offset = offset + numbytes;
                }

                // Keep track of how long we really slept compared to how
                // long we expected to sleep.  If we went over, subtract the
                // time over from the next sleep time.  This way we maintain
                // the proper average sleep time.  This can result in multiple
                // packets going out at once, potentially losing packets.
                if (packet_wait > overage) {
                    usleep(packet_wait - (int32_t)overage);
                }
                gettimeofday(&current_sent, NULL);
                tdiff = diff_usec(current_sent, last_sent);
                avgwait += tdiff;
                waitcnt++;
                if (packet_wait) overage += tdiff - packet_wait;
                last_sent = current_sent;

                fileseg->seq_num = htonl(block);
                send_data(finfo, packet, numbytes, encpacket);
                sent_blocks++;
            }
            if ((block % (blocksize * 8) == (blocksize * 8) - 1) ||
                    (block == finfo->blocks - 1)) {
                if ((pass == 1) || sent_blocks) {
                    current_naks = get_naks(finfo, pass, section, &alldone);
                    numnaks += current_naks;
                    if ((rate != -1) && (cc_count > 0)) {
                        if (!read_cc_config(cc_config)) {
                            log1(0, 0, "Error rereading congestion control "
                                       "config, using prior values");
                        }
                        adjust_rate(current_naks, sent_blocks);
                    }
                    overage = 0;
                    if (alldone) break;
                }
                sent_blocks = 0;
                gettimeofday(&last_sent, NULL);
                fileseg->section = htons(++section);
            }
        }
        if ((finfo->size == 0) && !alldone) {
            // If it's the end of the group, or an empty file, a DONE was
            // never sent, so send it now
            numnaks += get_naks(finfo, pass, section, &alldone);
        }
        if (finfo->file_id != 0) {
            log(0, 0, "Average wait time = %.2f us",
                       (waitcnt == 0) ? 0 : (float)avgwait / waitcnt);
            log(0, 0, "Received %d distinct NAKs for pass %d", numnaks, pass);
        }
        pass++;
    } while (!alldone);

    if ((finfo->file_id != 0) && (finfo->ftype == FTYPE_REG)) {
        close(file);
    }
    print_status(finfo, start_time);

    free(packet);
    free(encpacket);

    for (i = 0; i < destcount; i++) {
        if (quit_on_error) {
            // Check to see that all finished
            if ((destlist[i].status != DEST_DONE) &&
                    (destlist[i].clientcnt == -1)) {
                return 0;
            }
        } else {
            // Check to see if at least one finished
            if (destlist[i].status == DEST_DONE) {
                return 1;
            }
        }
    }
    if (quit_on_error) {
        return 1;
    } else {
        return 0;
    }
}