diff options
author | kientzle <kientzle@FreeBSD.org> | 2006-11-24 02:00:48 +0000 |
---|---|---|
committer | kientzle <kientzle@FreeBSD.org> | 2006-11-24 02:00:48 +0000 |
commit | 7d9bedda5f3fc686498476685785118426cc9b5c (patch) | |
tree | 1718edba490b96d4478714d3358a3b2dfc19fda7 /lib/libarchive/archive_read_open_file.c | |
parent | cceb584eb3c30510c29dc942777634c05bbd9434 (diff) | |
download | FreeBSD-src-7d9bedda5f3fc686498476685785118426cc9b5c.zip FreeBSD-src-7d9bedda5f3fc686498476685785118426cc9b5c.tar.gz |
New hooks for reading/writing archives to/from a FILE * or
an in-memory buffer.
PR: bin/86742
Diffstat (limited to 'lib/libarchive/archive_read_open_file.c')
-rw-r--r-- | lib/libarchive/archive_read_open_file.c | 177 |
1 files changed, 47 insertions, 130 deletions
diff --git a/lib/libarchive/archive_read_open_file.c b/lib/libarchive/archive_read_open_file.c index cefc286..cb18fc3 100644 --- a/lib/libarchive/archive_read_open_file.c +++ b/lib/libarchive/archive_read_open_file.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2003-2004 Tim Kientzle + * Copyright (c) 2003-2006 Tim Kientzle * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -48,12 +48,10 @@ __FBSDID("$FreeBSD$"); #include "archive.h" -struct read_file_data { - int fd; +struct read_FILE_data { + FILE *f; size_t block_size; void *buffer; - mode_t st_mode; /* Mode bits for opened file. */ - char filename[1]; /* Must be last! */ }; static int file_close(struct archive *, void *); @@ -62,91 +60,54 @@ static ssize_t file_read(struct archive *, void *, const void **buff); static ssize_t file_skip(struct archive *, void *, size_t request); int -archive_read_open_file(struct archive *a, const char *filename, - size_t block_size) +archive_read_open_FILE(struct archive *a, FILE *f) { - return (archive_read_open_filename(a, filename, block_size)); -} + struct read_FILE_data *mine; -int -archive_read_open_filename(struct archive *a, const char *filename, - size_t block_size) -{ - struct read_file_data *mine; - - if (filename == NULL || filename[0] == '\0') { - mine = (struct read_file_data *)malloc(sizeof(*mine)); - if (mine == NULL) { - archive_set_error(a, ENOMEM, "No memory"); - return (ARCHIVE_FATAL); - } - mine->filename[0] = '\0'; - } else { - mine = (struct read_file_data *)malloc(sizeof(*mine) + strlen(filename)); - if (mine == NULL) { - archive_set_error(a, ENOMEM, "No memory"); - return (ARCHIVE_FATAL); - } - strcpy(mine->filename, filename); + mine = (struct read_FILE_data *)malloc(sizeof(*mine)); + if (mine == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + return (ARCHIVE_FATAL); } - mine->block_size = block_size; - mine->buffer = NULL; - mine->fd = -1; - return (archive_read_open2(a, mine, file_open, file_read, file_skip, file_close)); + mine->block_size = 128 * 1024; + mine->buffer = malloc(mine->block_size); + if (mine->buffer == NULL) { + archive_set_error(a, ENOMEM, "No memory"); + free(mine); + return (ARCHIVE_FATAL); + } + mine->f = f; + return (archive_read_open2(a, mine, file_open, file_read, + file_skip, file_close)); } static int file_open(struct archive *a, void *client_data) { - struct read_file_data *mine = (struct read_file_data *)client_data; + struct read_FILE_data *mine = (struct read_FILE_data *)client_data; struct stat st; - mine->buffer = malloc(mine->block_size); - if (mine->buffer == NULL) { - archive_set_error(a, ENOMEM, "No memory"); - return (ARCHIVE_FATAL); - } - if (mine->filename[0] != '\0') - mine->fd = open(mine->filename, O_RDONLY); - else - mine->fd = 0; /* Fake "open" for stdin. */ - if (mine->fd < 0) { - archive_set_error(a, errno, "Failed to open '%s'", - mine->filename); - return (ARCHIVE_FATAL); - } - if (fstat(mine->fd, &st) == 0) { - /* If we're reading a file from disk, ensure that we don't - overwrite it with an extracted file. */ - if (S_ISREG(st.st_mode)) - archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); - /* Remember mode so close can decide whether to flush. */ - mine->st_mode = st.st_mode; - } else { - if (mine->filename[0] == '\0') - archive_set_error(a, errno, "Can't stat stdin"); - else - archive_set_error(a, errno, "Can't stat '%s'", - mine->filename); - return (ARCHIVE_FATAL); - } - return (0); + /* + * If we can't fstat() the file, it may just be that + * it's not a file. (FILE * objects can wrap many kinds + * of I/O streams.) + */ + if (fstat(fileno(mine->f), &st) == 0 && S_ISREG(st.st_mode)) + archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino); + + return (ARCHIVE_OK); } static ssize_t file_read(struct archive *a, void *client_data, const void **buff) { - struct read_file_data *mine = (struct read_file_data *)client_data; + struct read_FILE_data *mine = (struct read_FILE_data *)client_data; ssize_t bytes_read; *buff = mine->buffer; - bytes_read = read(mine->fd, mine->buffer, mine->block_size); + bytes_read = fread(mine->buffer, 1, mine->block_size, mine->f); if (bytes_read < 0) { - if (mine->filename[0] == '\0') - archive_set_error(a, errno, "Error reading stdin"); - else - archive_set_error(a, errno, "Error reading '%s'", - mine->filename); + archive_set_error(a, errno, "Error reading file"); } return (bytes_read); } @@ -154,77 +115,33 @@ file_read(struct archive *a, void *client_data, const void **buff) static ssize_t file_skip(struct archive *a, void *client_data, size_t request) { - struct read_file_data *mine = (struct read_file_data *)client_data; - off_t old_offset, new_offset; + struct read_FILE_data *mine = (struct read_FILE_data *)client_data; - /* Reduce request to the next smallest multiple of block_size */ - request = (request / mine->block_size) * mine->block_size; /* - * Hurray for lazy evaluation: if the first lseek fails, the second - * one will not be executed. + * Note: the 'fd' and 'filename' versions round the request + * down to a multiple of the block size to ensure proper + * operation on block-oriented media such as tapes. But stdio + * doesn't work with such media (it doesn't ensure blocking), + * so we don't need to bother. */ - if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) || - ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0)) +#if HAVE_FSEEKO + if (fseeko(mine->f, request, SEEK_CUR) != 0) +#else + if (fseek(mine->f, request, SEEK_CUR) != 0) +#endif { - if (errno == ESPIPE) - { - /* - * Failure to lseek() can be caused by the file - * descriptor pointing to a pipe, socket or FIFO. - * Return 0 here, so the compression layer will use - * read()s instead to advance the file descriptor. - * It's slower of course, but works as well. - */ - return (0); - } - /* - * There's been an error other than ESPIPE. This is most - * likely caused by a programmer error (too large request) - * or a corrupted archive file. - */ - if (mine->filename[0] == '\0') - /* - * Should never get here, since lseek() on stdin ought - * to return an ESPIPE error. - */ - archive_set_error(a, errno, "Error seeking in stdin"); - else - archive_set_error(a, errno, "Error seeking in '%s'", - mine->filename); - return (-1); + archive_set_error(a, errno, "Error skipping forward"); + return (ARCHIVE_FATAL); } - return (new_offset - old_offset); + return (request); } static int file_close(struct archive *a, void *client_data) { - struct read_file_data *mine = (struct read_file_data *)client_data; + struct read_FILE_data *mine = (struct read_FILE_data *)client_data; (void)a; /* UNUSED */ - - /* - * Sometimes, we should flush the input before closing. - * Regular files: faster to just close without flush. - * Devices: must not flush (user might need to - * read the "next" item on a non-rewind device). - * Pipes and sockets: must flush (otherwise, the - * program feeding the pipe or socket may complain). - * Here, I flush everything except for regular files and - * device nodes. - */ - if (!S_ISREG(mine->st_mode) - && !S_ISCHR(mine->st_mode) - && !S_ISBLK(mine->st_mode)) { - ssize_t bytesRead; - do { - bytesRead = read(mine->fd, mine->buffer, - mine->block_size); - } while (bytesRead > 0); - } - /* If a named file was opened, then it needs to be closed. */ - if (mine->filename[0] != '\0') - close(mine->fd); if (mine->buffer != NULL) free(mine->buffer); free(mine); |