Import libarchive-3.0.2.
[dragonfly.git] / contrib / libarchive / libarchive / archive_entry_sparse.c
1 /*-
2  * Copyright (c) 2003-2007 Tim Kientzle
3  * Copyright (c) 2010-2011 Michihiro NAKAJIMA
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
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$");
29
30 #include "archive.h"
31 #include "archive_entry.h"
32 #include "archive_private.h"
33 #include "archive_entry_private.h"
34
35 /*
36  * sparse handling
37  */
38
39 void
40 archive_entry_sparse_clear(struct archive_entry *entry)
41 {
42         struct ae_sparse *sp;
43
44         while (entry->sparse_head != NULL) {
45                 sp = entry->sparse_head->next;
46                 free(entry->sparse_head);
47                 entry->sparse_head = sp;
48         }
49         entry->sparse_tail = NULL;
50 }
51
52 void
53 archive_entry_sparse_add_entry(struct archive_entry *entry,
54         int64_t offset, int64_t length)
55 {
56         struct ae_sparse *sp;
57
58         if (offset < 0 || length < 0)
59                 /* Invalid value */
60                 return;
61         if (offset + length < 0 ||
62             offset + length > archive_entry_size(entry))
63                 /* A value of "length" parameter is too large. */
64                 return;
65         if ((sp = entry->sparse_tail) != NULL) {
66                 if (sp->offset + sp->length > offset)
67                         /* Invalid value. */
68                         return;
69                 if (sp->offset + sp->length == offset) {
70                         if (sp->offset + sp->length + length < 0)
71                                 /* A value of "length" parameter is
72                                  * too large. */
73                                 return;
74                         /* Expand existing sparse block size. */
75                         sp->length += length;
76                         return;
77                 }
78         }
79
80         if ((sp = (struct ae_sparse *)malloc(sizeof(*sp))) == NULL)
81                 /* XXX Error XXX */
82                 return;
83
84         sp->offset = offset;
85         sp->length = length;
86         sp->next = NULL;
87
88         if (entry->sparse_head == NULL)
89                 entry->sparse_head = entry->sparse_tail = sp;
90         else {
91                 /* Add a new sparse block to the tail of list. */
92                 if (entry->sparse_tail != NULL)
93                         entry->sparse_tail->next = sp;
94                 entry->sparse_tail = sp;
95         }
96 }
97
98
99 /*
100  * returns number of the sparse entries
101  */
102 int
103 archive_entry_sparse_count(struct archive_entry *entry)
104 {
105         struct ae_sparse *sp;
106         int count = 0;
107
108         for (sp = entry->sparse_head; sp != NULL; sp = sp->next)
109                 count++;
110
111         /*
112          * Sanity check if this entry is exactly sparse.
113          * If amount of sparse blocks is just one and it indicates the whole
114          * file data, we should remove it and return zero.
115          */
116         if (count == 1) {
117                 sp = entry->sparse_head;
118                 if (sp->offset == 0 &&
119                     sp->length >= archive_entry_size(entry)) {
120                         count = 0;
121                         archive_entry_sparse_clear(entry);
122                 }
123         }
124
125         return (count);
126 }
127
128 int
129 archive_entry_sparse_reset(struct archive_entry * entry)
130 {
131         entry->sparse_p = entry->sparse_head;
132
133         return archive_entry_sparse_count(entry);
134 }
135
136 int
137 archive_entry_sparse_next(struct archive_entry * entry,
138         int64_t *offset, int64_t *length)
139 {
140         if (entry->sparse_p) {
141                 *offset = entry->sparse_p->offset;
142                 *length = entry->sparse_p->length;
143
144                 entry->sparse_p = entry->sparse_p->next;
145
146                 return (ARCHIVE_OK);
147         } else {
148                 *offset = 0;
149                 *length = 0;
150                 return (ARCHIVE_WARN);
151         }
152 }
153
154 /*
155  * end of sparse handling
156  */