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 	la_int64_t offset, la_int64_t length)
55 {
56 	struct ae_sparse *sp;
57 
58 	if (offset < 0 || length < 0)
59 		/* Invalid value */
60 		return;
61 	if (offset > INT64_MAX - length ||
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 	la_int64_t *offset, la_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  */
157