#include "zipint.h"
#include "zip_source_file.h"
#include "zip_source_file_stdio.h"
#include <stdlib.h>
#include <sys/stat.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_CLONEFILE
#include <sys/attr.h>
#include <sys/clonefile.h>
#define CAN_CLONE
#endif
#ifdef HAVE_FICLONERANGE
#include <linux/fs.h>
#include <sys/ioctl.h>
#define CAN_CLONE
#endif
static zip_int64_t _zip_stdio_op_commit_write(zip_source_file_context_t *ctx);
static zip_int64_t _zip_stdio_op_create_temp_output(zip_source_file_context_t *ctx);
#ifdef CAN_CLONE
static zip_int64_t _zip_stdio_op_create_temp_output_cloning(zip_source_file_context_t *ctx, zip_uint64_t offset);
#endif
static bool _zip_stdio_op_open(zip_source_file_context_t *ctx);
static zip_int64_t _zip_stdio_op_remove(zip_source_file_context_t *ctx);
static void _zip_stdio_op_rollback_write(zip_source_file_context_t *ctx);
static char *_zip_stdio_op_strdup(zip_source_file_context_t *ctx, const char *string);
static zip_int64_t _zip_stdio_op_write(zip_source_file_context_t *ctx, const void *data, zip_uint64_t len);
static zip_source_file_operations_t ops_stdio_named = …;
ZIP_EXTERN zip_source_t *
zip_source_file(zip_t *za, const char *fname, zip_uint64_t start, zip_int64_t len) { … }
ZIP_EXTERN zip_source_t *
zip_source_file_create(const char *fname, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { … }
static zip_int64_t
_zip_stdio_op_commit_write(zip_source_file_context_t *ctx) { … }
static zip_int64_t
_zip_stdio_op_create_temp_output(zip_source_file_context_t *ctx) { … }
#ifdef CAN_CLONE
static zip_int64_t
_zip_stdio_op_create_temp_output_cloning(zip_source_file_context_t *ctx, zip_uint64_t offset) {
char *temp;
FILE *tfp;
if (offset > ZIP_OFF_MAX) {
zip_error_set(&ctx->error, ZIP_ER_SEEK, E2BIG);
return -1;
}
size_t temp_size = strlen(ctx->fname) + 8;
if ((temp = (char *)malloc(temp_size)) == NULL) {
zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
return -1;
}
snprintf(temp, temp_size, "%s.XXXXXX", ctx->fname);
#ifdef HAVE_CLONEFILE
#ifndef __clang_analyzer__
if (mktemp(temp) == NULL) {
zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
free(temp);
return -1;
}
#endif
if (clonefile(ctx->fname, temp, 0) < 0) {
zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
free(temp);
return -1;
}
if ((tfp = _zip_fopen_close_on_exec(temp, true)) == NULL) {
zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
(void)remove(temp);
free(temp);
return -1;
}
#else
{
int fd;
struct file_clone_range range;
struct stat st;
if (fstat(fileno(ctx->f), &st) < 0) {
zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
free(temp);
return -1;
}
if ((fd = mkstemp(temp)) < 0) {
zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
free(temp);
return -1;
}
range.src_fd = fileno(ctx->f);
range.src_offset = 0;
range.src_length = ((offset + st.st_blksize - 1) / st.st_blksize) * st.st_blksize;
if (range.src_length > st.st_size) {
range.src_length = 0;
}
range.dest_offset = 0;
if (ioctl(fd, FICLONERANGE, &range) < 0) {
zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
(void)close(fd);
(void)remove(temp);
free(temp);
return -1;
}
if ((tfp = fdopen(fd, "r+b")) == NULL) {
zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
(void)close(fd);
(void)remove(temp);
free(temp);
return -1;
}
}
#endif
if (ftruncate(fileno(tfp), (off_t)offset) < 0) {
(void)fclose(tfp);
(void)remove(temp);
free(temp);
return -1;
}
if (fseeko(tfp, (off_t)offset, SEEK_SET) < 0) {
(void)fclose(tfp);
(void)remove(temp);
free(temp);
zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
}
ctx->fout = tfp;
ctx->tmpname = temp;
return 0;
}
#endif
static bool
_zip_stdio_op_open(zip_source_file_context_t *ctx) { … }
static zip_int64_t
_zip_stdio_op_remove(zip_source_file_context_t *ctx) { … }
static void
_zip_stdio_op_rollback_write(zip_source_file_context_t *ctx) { … }
static char *
_zip_stdio_op_strdup(zip_source_file_context_t *ctx, const char *string) { … }
static zip_int64_t
_zip_stdio_op_write(zip_source_file_context_t *ctx, const void *data, zip_uint64_t len) { … }