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