static gboolean flush_buffer( amar_t *archive, GError **error) { if (archive->buf_len) { if (full_write(archive->fd, archive->buf, archive->buf_len) != archive->buf_len) { g_set_error(error, amar_error_quark(), errno, "Error writing to amanda archive: %s", strerror(errno)); return FALSE; } archive->buf_len = 0; } return TRUE; }
static gboolean write_record( amar_t *archive, amar_file_t *file, guint16 attrid, gboolean eoa, gpointer data, gsize data_size, GError **error) { /* the buffer always has room for a new record header */ MKRECORD(archive->buf + archive->buf_len, file->filenum, attrid, data_size, eoa); archive->buf_len += RECORD_SIZE; /* is it worth copying this record into the buffer? */ if (archive->buf_len + RECORD_SIZE + data_size < WRITE_BUFFER_SIZE - RECORD_SIZE) { /* yes, it is */ if (data_size) memcpy(archive->buf + archive->buf_len, data, data_size); archive->buf_len += data_size; } else { /* no, it's not */ struct iovec iov[2]; /* flush the buffer and write the new data, all in one syscall */ iov[0].iov_base = archive->buf; iov[0].iov_len = archive->buf_len; iov[1].iov_base = data; iov[1].iov_len = data_size; if (full_writev(archive->fd, iov, 2) < 0) { g_set_error(error, amar_error_quark(), errno, "Error writing to amanda archive: %s", strerror(errno)); return FALSE; } archive->buf_len = 0; } archive->position += data_size + RECORD_SIZE; file->size += data_size + RECORD_SIZE; return TRUE; }
amar_file_t * amar_new_file( amar_t *archive, char *filename_buf, gsize filename_len, off_t *header_offset, GError **error) { amar_file_t *file = NULL; g_assert(archive->mode == O_WRONLY); g_assert(filename_buf != NULL); /* set filename_len if it wasn't specified */ if (!filename_len) filename_len = strlen(filename_buf); g_assert(filename_len != 0); if (filename_len > MAX_RECORD_DATA_SIZE) { g_set_error(error, amar_error_quark(), ENOSPC, "filename is too long for an amanda archive"); return NULL; } /* pick a new, unused filenum */ if (g_hash_table_size(archive->files) == 65535) { g_set_error(error, amar_error_quark(), ENOSPC, "No more file numbers available"); return NULL; } do { gint filenum; archive->maxfilenum++; /* MAGIC_FILENUM can't be used because it matches the header record text */ if (archive->maxfilenum == MAGIC_FILENUM) { continue; } /* see if this fileid is already in use */ filenum = archive->maxfilenum; if (g_hash_table_lookup(archive->files, &filenum)) continue; } while (0); file = g_new0(amar_file_t, 1); if (!file) { g_set_error(error, amar_error_quark(), ENOSPC, "No more memory"); return NULL; } file->archive = archive; file->filenum = archive->maxfilenum; file->attributes = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, g_free); g_hash_table_insert(archive->files, &file->filenum, file); /* record the current position and write a header there, if desired */ if (header_offset) { *header_offset = archive->position; if (!write_header(archive, error)) goto error_exit; } /* add a filename record */ if (!write_record(archive, file->filenum, AMAR_ATTR_FILENAME, 1, filename_buf, filename_len, error)) goto error_exit; return file; error_exit: if (file) { g_hash_table_remove(archive->files, &file->filenum); g_hash_table_destroy(file->attributes); g_free(file); } return NULL; }