Merge from vendor branch LIBARCHIVE:
[dragonfly.git] / contrib / libarchive-1.3.1 / libarchive / archive_read_open_fd.c
1 /*-
2  * Copyright (c) 2003-2004 Tim Kientzle
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "archive_platform.h"
28 __FBSDID("$FreeBSD: src/lib/libarchive/archive_read_open_fd.c,v 1.6 2006/07/30 00:29:00 kientzle Exp $");
29
30 #include <sys/stat.h>
31 #include <errno.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35
36 #include "archive.h"
37
38 struct read_fd_data {
39         int      fd;
40         size_t   block_size;
41         void    *buffer;
42 };
43
44 static int      file_close(struct archive *, void *);
45 static int      file_open(struct archive *, void *);
46 static ssize_t  file_read(struct archive *, void *, const void **buff);
47 static ssize_t  file_skip(struct archive *, void *, size_t request);
48
49 int
50 archive_read_open_fd(struct archive *a, int fd, size_t block_size)
51 {
52         struct read_fd_data *mine;
53
54         mine = malloc(sizeof(*mine));
55         if (mine == NULL) {
56                 archive_set_error(a, ENOMEM, "No memory");
57                 return (ARCHIVE_FATAL);
58         }
59         mine->block_size = block_size;
60         mine->buffer = malloc(mine->block_size);
61         if (mine->buffer == NULL) {
62                 archive_set_error(a, ENOMEM, "No memory");
63                 free(mine);
64                 return (ARCHIVE_FATAL);
65         }
66         mine->fd = fd;
67         return (archive_read_open2(a, mine, file_open, file_read, file_skip, file_close));
68 }
69
70 static int
71 file_open(struct archive *a, void *client_data)
72 {
73         struct read_fd_data *mine = client_data;
74         struct stat st;
75
76         if (fstat(mine->fd, &st) != 0) {
77                 archive_set_error(a, errno, "Can't stat fd %d", mine->fd);
78                 return (ARCHIVE_FATAL);
79         }
80
81         if (S_ISREG(st.st_mode))
82                 archive_read_extract_set_skip_file(a, st.st_dev, st.st_ino);
83
84         return (ARCHIVE_OK);
85 }
86
87 static ssize_t
88 file_read(struct archive *a, void *client_data, const void **buff)
89 {
90         struct read_fd_data *mine = client_data;
91         ssize_t bytes_read;
92
93         *buff = mine->buffer;
94         bytes_read = read(mine->fd, mine->buffer, mine->block_size);
95         if (bytes_read < 0) {
96                 archive_set_error(a, errno, "Error reading fd %d", mine->fd);
97         }
98         return (bytes_read);
99 }
100
101 static ssize_t
102 file_skip(struct archive *a, void *client_data, size_t request)
103 {
104         struct read_fd_data *mine = client_data;
105         off_t old_offset, new_offset;
106         
107         /* Reduce request to the next smallest multiple of block_size */
108         request = (request / mine->block_size) * mine->block_size;
109         /*
110          * Hurray for lazy evaluation: if the first lseek fails, the second
111          * one will not be executed.
112          */
113         if (((old_offset = lseek(mine->fd, 0, SEEK_CUR)) < 0) ||
114             ((new_offset = lseek(mine->fd, request, SEEK_CUR)) < 0))
115         {
116                 if (errno == ESPIPE)
117                 {
118                         /*
119                          * Failure to lseek() can be caused by the file
120                          * descriptor pointing to a pipe, socket or FIFO.
121                          * Return 0 here, so the compression layer will use
122                          * read()s instead to advance the file descriptor.
123                          * It's slower of course, but works as well.
124                          */
125                         return (0);
126                 }
127                 /*
128                  * There's been an error other than ESPIPE. This is most
129                  * likely caused by a programmer error (too large request)
130                  * or a corrupted archive file.
131                  */
132                 archive_set_error(a, errno, "Error seeking");
133                 return (-1);
134         }
135         return (new_offset - old_offset);
136 }
137
138 static int
139 file_close(struct archive *a, void *client_data)
140 {
141         struct read_fd_data *mine = client_data;
142
143         (void)a; /* UNUSED */
144         if (mine->buffer != NULL)
145                 free(mine->buffer);
146         free(mine);
147         return (ARCHIVE_OK);
148 }