/* * Write out the buffer to the backing file */ static int databuf_flush( struct databuf * db) { struct cmdargs *cmdargs = NULL; int rc = 1; size_t size_to_write; size_t written; off_t left_in_chunk; char *arg_filename = NULL; char *new_filename = NULL; char *tmp_filename = NULL; char sequence[NUM_STR_SIZE]; int newfd; filetype_t save_type; char *q; int a; char *pc; /* * If there's no data, do nothing. */ if (db->dataout >= db->datain) { goto common_exit; } /* * See if we need to split this file. */ while (db->split_size > (off_t)0 && dumpsize >= db->split_size) { if( db->use == (off_t)0 ) { /* * Probably no more space on this disk. Request some more. */ putresult(RQ_MORE_DISK, "%s\n", handle); cmdargs = getcmd(); if(command_in_transit == NULL && (cmdargs->cmd == DONE || cmdargs->cmd == TRYAGAIN || cmdargs->cmd == FAILED)) { command_in_transit = cmdargs; cmdargs = getcmd(); } if(cmdargs->cmd == CONTINUE) { /* * CONTINUE * serial * filename * chunksize * use */ a = 2; /* skip CONTINUE and serial */ if(a >= cmdargs->argc) { error(_("error [chunker CONTINUE: not enough args: filename]")); /*NOTREACHED*/ } arg_filename = newstralloc(arg_filename, cmdargs->argv[a++]); if(a >= cmdargs->argc) { error(_("error [chunker CONTINUE: not enough args: chunksize]")); /*NOTREACHED*/ } db->chunk_size = OFF_T_ATOI(cmdargs->argv[a++]); db->chunk_size = am_floor(db->chunk_size, (off_t)DISK_BLOCK_KB); if(a >= cmdargs->argc) { error(_("error [chunker CONTINUE: not enough args: use]")); /*NOTREACHED*/ } db->use = OFF_T_ATOI(cmdargs->argv[a++]); if(a != cmdargs->argc) { error(_("error [chunker CONTINUE: too many args: %d != %d]"), cmdargs->argc, a); /*NOTREACHED*/ } if(strcmp(db->filename, arg_filename) == 0) { /* * Same disk, so use what room is left up to the * next chunk boundary or the amount we were given, * whichever is less. */ left_in_chunk = db->chunk_size - filesize; if(left_in_chunk > db->use) { db->split_size += db->use; db->use = (off_t)0; } else { db->split_size += left_in_chunk; db->use -= left_in_chunk; } if(left_in_chunk > (off_t)0) { /* * We still have space in this chunk. */ break; } } else { /* * Different disk, so use new file. */ db->filename = newstralloc(db->filename, arg_filename); } } else if(cmdargs->cmd == ABORT) { abort_pending = 1; errstr = newstralloc(errstr, cmdargs->argv[1]); putresult(ABORT_FINISHED, "%s\n", handle); rc = 0; goto common_exit; } else { if(cmdargs->argc >= 1) { q = quote_string(cmdargs->argv[0]); } else { q = stralloc(_("(no input?)")); } error(_("error [bad command after RQ-MORE-DISK: \"%s\"]"), q); /*NOTREACHED*/ } } /* * Time to use another file. */ /* * First, open the new chunk file, and give it a new header * that has no cont_filename pointer. */ g_snprintf(sequence, SIZEOF(sequence), "%d", db->filename_seq); new_filename = newvstralloc(new_filename, db->filename, ".", sequence, NULL); tmp_filename = newvstralloc(tmp_filename, new_filename, ".tmp", NULL); pc = strrchr(tmp_filename, '/'); g_assert(pc != NULL); /* Only a problem if db->filename has no /. */ *pc = '\0'; mkholdingdir(tmp_filename); *pc = '/'; newfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600); if (newfd == -1) { int save_errno = errno; char *m; if(save_errno == ENOSPC) { putresult(NO_ROOM, "%s %lld\n", handle, (long long)(db->use+db->split_size-dumpsize)); db->use = (off_t)0; /* force RQ_MORE_DISK */ db->split_size = dumpsize; continue; } m = vstrallocf(_("creating chunk holding file \"%s\": %s"), tmp_filename, strerror(errno)); errstr = quote_string(m); amfree(m); aclose(db->fd); rc = 0; goto common_exit; } save_type = file.type; file.type = F_CONT_DUMPFILE; file.cont_filename[0] = '\0'; if(write_tapeheader(newfd, &file)) { int save_errno = errno; char *m; aclose(newfd); if(save_errno == ENOSPC) { putresult(NO_ROOM, "%s %lld\n", handle, (long long)(db->use+db->split_size-dumpsize)); db->use = (off_t)0; /* force RQ_MORE DISK */ db->split_size = dumpsize; continue; } m = vstrallocf(_("write_tapeheader file %s: %s"), tmp_filename, strerror(errno)); errstr = quote_string(m); amfree(m); rc = 0; goto common_exit; } /* * Now, update the header of the current file to point * to the next chunk, and then close it. */ if (lseek(db->fd, (off_t)0, SEEK_SET) < (off_t)0) { char *m = vstrallocf(_("lseek holding file %s: %s"), db->filename, strerror(errno)); errstr = quote_string(m); amfree(m); aclose(newfd); rc = 0; goto common_exit; } file.type = save_type; strncpy(file.cont_filename, new_filename, SIZEOF(file.cont_filename)); file.cont_filename[SIZEOF(file.cont_filename)-1] = '\0'; if(write_tapeheader(db->fd, &file)) { char * m = vstrallocf(_("write_tapeheader file \"%s\": %s"), db->filename, strerror(errno)); errstr = quote_string(m); amfree(m); aclose(newfd); unlink(tmp_filename); rc = 0; goto common_exit; } file.type = F_CONT_DUMPFILE; /* * Now shift the file descriptor. */ aclose(db->fd); db->fd = newfd; newfd = -1; /* * Update when we need to chunk again */ if(db->use <= (off_t)DISK_BLOCK_KB) { /* * Cheat and use one more block than allowed so we can make * some progress. */ db->split_size += (off_t)(2 * DISK_BLOCK_KB); db->use = (off_t)0; } else if(db->chunk_size > db->use) { db->split_size += db->use; db->use = (off_t)0; } else { db->split_size += db->chunk_size; db->use -= db->chunk_size; } amfree(tmp_filename); amfree(new_filename); dumpsize += (off_t)DISK_BLOCK_KB; filesize = (off_t)DISK_BLOCK_KB; headersize += DISK_BLOCK_KB; db->filename_seq++; } /* * Write out the buffer */ size_to_write = (size_t)(db->datain - db->dataout); written = full_write(db->fd, db->dataout, size_to_write); if (written > 0) { db->dataout += written; dumpbytes += (off_t)written; } dumpsize += (dumpbytes / (off_t)1024); filesize += (dumpbytes / (off_t)1024); dumpbytes %= 1024; if (written < size_to_write) { if (errno != ENOSPC) { char *m = vstrallocf(_("data write: %s"), strerror(errno)); errstr = quote_string(m); amfree(m); rc = 0; goto common_exit; } /* * NO-ROOM is informational only. Later, RQ_MORE_DISK will be * issued to use another holding disk. */ putresult(NO_ROOM, "%s %lld\n", handle, (long long)(db->use+db->split_size-dumpsize)); db->use = (off_t)0; /* force RQ_MORE_DISK */ db->split_size = dumpsize; goto common_exit; } if (db->datain == db->dataout) { /* * We flushed the whole buffer so reset to use it all. */ db->datain = db->dataout = db->buf; } common_exit: if (cmdargs) free_cmdargs(cmdargs); amfree(new_filename); /*@i@*/ amfree(tmp_filename); amfree(arg_filename); return rc; }
static gpointer holding_thread( gpointer data) { XferDestHolding *self = XFER_DEST_HOLDING(data); XferElement *elt = XFER_ELEMENT(self); XMsg *msg; gchar *mesg = NULL; GTimer *timer = g_timer_new(); DBG(1, "(this is the holding thread)"); /* This is the outer loop, that loops once for each holding file or * CONTINUE command */ g_mutex_lock(self->state_mutex); while (1) { gboolean done; /* wait until the main thread un-pauses us, and check that we have * the relevant holding info available */ while (self->paused && !elt->cancelled) { DBG(9, "waiting to be unpaused"); g_cond_wait(self->state_cond, self->state_mutex); } DBG(9, "holding_thread done waiting"); if (elt->cancelled) break; self->data_bytes_written = 0; self->header_bytes_written = 0; /* new holding file */ if (self->filename == NULL || strcmp(self->filename, self->new_filename) != 0) { char *tmp_filename; char *pc; int fd; ssize_t write_header_size; if (self->use_bytes < HEADER_BLOCK_BYTES) { self->chunk_status = CHUNK_NO_ROOM; goto no_room; } tmp_filename = g_strjoin(NULL, self->new_filename, ".tmp", NULL); pc = strrchr(tmp_filename, '/'); g_assert(pc != NULL); *pc = '\0'; mkholdingdir(tmp_filename); *pc = '/'; fd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600); if (fd < 0) { self->chunk_status = CHUNK_NO_ROOM; g_free(mesg); mesg = g_strdup_printf("Failed to open '%s': %s", tmp_filename, strerror(errno)); g_free(tmp_filename); goto no_room; } if (self->filename == NULL) { self->chunk_header->type = F_DUMPFILE; } else { self->chunk_header->type = F_CONT_DUMPFILE; } self->chunk_header->cont_filename[0] = '\0'; write_header_size = write_header(self, fd); if (write_header_size != HEADER_BLOCK_BYTES) { self->chunk_status = CHUNK_NO_ROOM; mesg = g_strdup_printf("Failed to write header to '%s': %s", tmp_filename, strerror(errno)); close(fd); unlink(tmp_filename); g_free(tmp_filename); goto no_room; } g_free(tmp_filename); self->use_bytes -= HEADER_BLOCK_BYTES; /* rewrite old_header */ if (self->filename && strcmp(self->filename, self->new_filename) != 0) { close_chunk(self, self->new_filename); } self->filename = self->new_filename; self->new_filename = NULL; self->fd = fd; self->header_bytes_written = HEADER_BLOCK_BYTES; self->chunk_offset = HEADER_BLOCK_BYTES; } DBG(2, "beginning to write chunk"); done = holding_thread_write_chunk(self); DBG(2, "done writing chunk"); if (!done) /* cancelled */ break; no_room: msg = xmsg_new(XFER_ELEMENT(self), XMSG_CHUNK_DONE, 0); msg->header_size = self->header_bytes_written; msg->data_size = self->data_bytes_written; msg->no_room = (self->chunk_status == CHUNK_NO_ROOM); if (mesg) { msg->message = mesg; mesg = NULL; } xfer_queue_message(elt->xfer, msg); /* pause ourselves and await instructions from the main thread */ self->paused = TRUE; /* if this is the last part, we're done with the chunk loop */ if (self->chunk_status == CHUNK_EOF) { break; } } g_mutex_unlock(self->state_mutex); g_debug("sending XMSG_CRC message"); g_debug("xfer-dest-holding CRC: %08x size: %lld", crc32_finish(&elt->crc), (long long)elt->crc.size); msg = xmsg_new(XFER_ELEMENT(self), XMSG_CRC, 0); msg->crc = crc32_finish(&elt->crc); msg->size = elt->crc.size; xfer_queue_message(elt->xfer, msg); msg = xmsg_new(XFER_ELEMENT(self), XMSG_DONE, 0); msg->duration = g_timer_elapsed(timer, NULL); g_timer_destroy(timer); /* tell the main thread we're done */ xfer_queue_message(elt->xfer, msg); return NULL; }
/* * Returns a file descriptor to the incoming port * on success, or -1 on error. */ static int startup_chunker( char * filename, off_t use, off_t chunksize, struct databuf * db) { int infd, outfd; char *tmp_filename, *pc; in_port_t data_port; int data_socket; int result; struct addrinfo *res; data_port = 0; if ((result = resolve_hostname("localhost", 0, &res, NULL) != 0)) { errstr = newvstrallocf(errstr, _("could not resolve localhost: %s"), gai_strerror(result)); return -1; } data_socket = stream_server(res->ai_family, &data_port, 0, STREAM_BUFSIZE, 0); if (res) freeaddrinfo(res); if(data_socket < 0) { errstr = vstrallocf(_("error creating stream server: %s"), strerror(errno)); return -1; } putresult(PORT, "%d\n", data_port); infd = stream_accept(data_socket, CONNECT_TIMEOUT, 0, STREAM_BUFSIZE); aclose(data_socket); if(infd == -1) { errstr = vstrallocf(_("error accepting stream: %s"), strerror(errno)); return -1; } tmp_filename = vstralloc(filename, ".tmp", NULL); pc = strrchr(tmp_filename, '/'); g_assert(pc != NULL); *pc = '\0'; mkholdingdir(tmp_filename); *pc = '/'; if ((outfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600)) < 0) { int save_errno = errno; char *m = vstrallocf(_("holding file \"%s\": %s"), tmp_filename, strerror(errno)); errstr = quote_string(m); amfree(m); amfree(tmp_filename); aclose(infd); if(save_errno == ENOSPC) { putresult(NO_ROOM, "%s %lld\n", handle, (long long)use); return -2; } else { return -1; } } amfree(tmp_filename); databuf_init(db, outfd, filename, use, chunksize); db->filename_seq++; return infd; }