1 /* nbdkit
2 * Copyright (C) 2019 Red Hat Inc.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * * 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 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include <config.h>
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdint.h>
38 #include <stdbool.h>
39 #include <string.h>
40 #include <inttypes.h>
41 #include <errno.h>
42 #include <assert.h>
43
44 #include "minmax.h"
45 #include "vector.h"
46
47 #include "internal.h"
48
49 /* Cap nr_extents to avoid sending over-large replies to the client,
50 * and to avoid a plugin with frequent alternations consuming too much
51 * memory.
52 */
53 #define MAX_EXTENTS (1 * 1024 * 1024)
54
55 /* Appendable list of extents. */
56 DEFINE_VECTOR_TYPE(extents, struct nbdkit_extent);
57
58 struct nbdkit_extents {
59 extents extents;
60
61 uint64_t start, end; /* end is one byte beyond the end of the range */
62
63 /* Where we expect the next extent to be added. If
64 * nbdkit_add_extent has never been called this is -1. Note this
65 * field is updated even if we don't actually add the extent because
66 * it's used to check for API violations.
67 */
68 int64_t next;
69 };
70
71 struct nbdkit_extents *
nbdkit_extents_new(uint64_t start,uint64_t end)72 nbdkit_extents_new (uint64_t start, uint64_t end)
73 {
74 struct nbdkit_extents *r;
75
76 if (start > INT64_MAX || end > INT64_MAX) {
77 nbdkit_error ("nbdkit_extents_new: "
78 "start (%" PRIu64 ") or end (%" PRIu64 ") > INT64_MAX",
79 start, end);
80 errno = ERANGE;
81 return NULL;
82 }
83
84 /* 0-length ranges are possible, so start == end is not an error. */
85 if (start > end) {
86 nbdkit_error ("nbdkit_extents_new: "
87 "start (%" PRIu64 ") >= end (%" PRIu64 ")",
88 start, end);
89 errno = ERANGE;
90 return NULL;
91 }
92
93 r = malloc (sizeof *r);
94 if (r == NULL) {
95 nbdkit_error ("nbdkit_extents_new: malloc: %m");
96 return NULL;
97 }
98 r->extents = (extents) empty_vector;
99 r->start = start;
100 r->end = end;
101 r->next = -1;
102 return r;
103 }
104
105 void
nbdkit_extents_free(struct nbdkit_extents * exts)106 nbdkit_extents_free (struct nbdkit_extents *exts)
107 {
108 if (exts) {
109 free (exts->extents.ptr);
110 free (exts);
111 }
112 }
113
114 size_t
nbdkit_extents_count(const struct nbdkit_extents * exts)115 nbdkit_extents_count (const struct nbdkit_extents *exts)
116 {
117 return exts->extents.size;
118 }
119
120 struct nbdkit_extent
nbdkit_get_extent(const struct nbdkit_extents * exts,size_t i)121 nbdkit_get_extent (const struct nbdkit_extents *exts, size_t i)
122 {
123 assert (i < exts->extents.size);
124 return exts->extents.ptr[i];
125 }
126
127 /* Insert *e in the list at the end. */
128 static int
append_extent(struct nbdkit_extents * exts,const struct nbdkit_extent * e)129 append_extent (struct nbdkit_extents *exts, const struct nbdkit_extent *e)
130 {
131 if (extents_append (&exts->extents, *e) == -1) {
132 nbdkit_error ("nbdkit_add_extent: realloc: %m");
133 return -1;
134 }
135
136 return 0;
137 }
138
139 int
nbdkit_add_extent(struct nbdkit_extents * exts,uint64_t offset,uint64_t length,uint32_t type)140 nbdkit_add_extent (struct nbdkit_extents *exts,
141 uint64_t offset, uint64_t length, uint32_t type)
142 {
143 uint64_t overlap;
144
145 /* Extents must be added in strictly ascending, contiguous order. */
146 if (exts->next >= 0 && exts->next != offset) {
147 nbdkit_error ("nbdkit_add_extent: "
148 "extents must be added in ascending order and "
149 "must be contiguous");
150 errno = ERANGE;
151 return -1;
152 }
153 exts->next = offset + length;
154
155 /* Ignore zero-length extents. */
156 if (length == 0)
157 return 0;
158
159 /* Ignore extents beyond the end of the range, or if list is full. */
160 if (offset >= exts->end || exts->extents.size >= MAX_EXTENTS)
161 return 0;
162
163 /* Shorten extents that overlap the end of the range. */
164 if (offset + length > exts->end) {
165 overlap = offset + length - exts->end;
166 length -= overlap;
167 }
168
169 if (exts->extents.size == 0) {
170 /* If there are no existing extents, and the new extent is
171 * entirely before start, ignore it.
172 */
173 if (offset + length <= exts->start)
174 return 0;
175
176 /* If there are no existing extents, and the new extent is after
177 * start, then this is a bug in the plugin.
178 */
179 if (offset > exts->start) {
180 nbdkit_error ("nbdkit_add_extent: "
181 "first extent must not be > start (%" PRIu64 ")",
182 exts->start);
183 errno = ERANGE;
184 return -1;
185 }
186
187 /* If there are no existing extents, and the new extent overlaps
188 * start, truncate it so it starts at start.
189 */
190 overlap = exts->start - offset;
191 length -= overlap;
192 offset += overlap;
193 }
194
195 /* If we get here we are going to either add or extend. */
196 if (exts->extents.size > 0 &&
197 exts->extents.ptr[exts->extents.size-1].type == type) {
198 /* Coalesce with the last extent. */
199 exts->extents.ptr[exts->extents.size-1].length += length;
200 return 0;
201 }
202 else {
203 /* Add a new extent. */
204 const struct nbdkit_extent e =
205 { .offset = offset, .length = length, .type = type };
206 return append_extent (exts, &e);
207 }
208 }
209