Add missing closing tag for para element
[nbd.git] / nbdsrv.c
blob400c152952f7a15b811dd40b5ce8b58ccf079a3a
1 #include "config.h"
2 #include "nbd-debug.h"
4 #include <nbdsrv.h>
6 #include <assert.h>
7 #include <ctype.h>
8 #include <netdb.h>
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <syslog.h>
13 #include <unistd.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <treefiles.h>
19 #include "backend.h"
20 #ifdef HAVE_SYS_MOUNT_H
21 #include <sys/mount.h>
22 #endif
24 #define LINELEN 256 /**< Size of static buffer used to read the
25 authorization file (yuck) */
27 #include <cliserv.h>
29 bool address_matches(const char* mask, const struct sockaddr* addr, GError** err) {
30 struct addrinfo *res, *aitmp, hints;
31 char *masksep;
32 char privmask[strlen(mask)+1];
33 int masklen;
34 int addrlen = addr->sa_family == AF_INET ? 4 : 16;
36 assert(addr->sa_family == AF_INET || addr->sa_family == AF_INET6);
38 strcpy(privmask, mask);
40 memset(&hints, 0, sizeof(hints));
41 hints.ai_family = AF_UNSPEC;
42 hints.ai_flags = AI_NUMERICHOST;
44 if((masksep = strchr(privmask, '/'))) {
45 *masksep = '\0';
46 masklen = strtol(++masksep, NULL, 10);
47 } else {
48 masklen = addrlen * 8;
51 int e;
52 if((e = getaddrinfo(privmask, NULL, &hints, &res))) {
53 g_set_error(err, NBDS_ERR, NBDS_ERR_GAI, "could not parse netmask line: %s", gai_strerror(e));
54 return false;
56 aitmp = res;
57 while(res) {
58 const uint8_t* byte_s;
59 uint8_t* byte_t;
60 uint8_t mask = 0;
61 int len_left = masklen;
62 if(res->ai_family != addr->sa_family) {
63 goto next;
65 switch(addr->sa_family) {
66 case AF_INET:
67 byte_s = (const uint8_t*)(&(((struct sockaddr_in*)addr)->sin_addr));
68 byte_t = (uint8_t*)(&(((struct sockaddr_in*)(res->ai_addr))->sin_addr));
69 break;
70 case AF_INET6:
71 byte_s = (const uint8_t*)(&(((struct sockaddr_in6*)addr)->sin6_addr));
72 byte_t = (uint8_t*)(&(((struct sockaddr_in6*)(res->ai_addr))->sin6_addr));
73 break;
75 while(len_left >= 8) {
76 if(*byte_s != *byte_t) {
77 goto next;
79 byte_s++; byte_t++;
80 len_left -= 8;
82 if(len_left) {
83 mask = getmaskbyte(len_left);
84 if((*byte_s & mask) != (*byte_t & mask)) {
85 goto next;
88 freeaddrinfo(aitmp);
89 return true;
90 next:
91 res = res->ai_next;
93 freeaddrinfo(aitmp);
94 return false;
97 uint8_t getmaskbyte(int masklen) {
98 if(masklen >= 8) {
99 return 0xFF;
101 uint8_t retval = 0;
102 for(int i = 7; i + masklen > 7; i--) {
103 retval |= 1 << i;
106 return retval;
109 int authorized_client(CLIENT *opts) {
110 FILE *f ;
111 char line[LINELEN];
113 if (opts->server->authname == NULL) {
114 msg(LOG_INFO, "No authorization file, granting access.");
115 return 1;
118 if ((f=fopen(opts->server->authname,"r"))==NULL) {
119 msg(LOG_INFO, "Can't open authorization file %s (%s).",
120 opts->server->authname, strerror(errno));
121 return 1 ;
124 while (fgets(line,LINELEN,f)!=NULL) {
125 char* pos;
126 /* Drop comments */
127 if((pos = strchr(line, '#'))) {
128 *pos = '\0';
130 /* Skip whitespace */
131 pos = line;
132 while((*pos) && isspace(*pos)) {
133 pos++;
135 /* Skip content-free lines */
136 if(!(*pos)) {
137 continue;
139 if(address_matches(line, (struct sockaddr*)&opts->clientaddr, NULL)) {
140 fclose(f);
141 return 1;
144 fclose(f);
145 return 0;
149 * duplicate server
150 * @param s the old server we want to duplicate
151 * @return new duplicated server
153 SERVER* dup_serve(const SERVER *const s) {
154 SERVER *serve = NULL;
156 serve=g_new0(SERVER, 1);
157 if(serve == NULL)
158 return NULL;
160 if(s->exportname)
161 serve->exportname = g_strdup(s->exportname);
163 serve->expected_size = s->expected_size;
165 if(s->listenaddr)
166 serve->listenaddr = g_strdup(s->listenaddr);
168 if(s->authname)
169 serve->authname = g_strdup(s->authname);
171 serve->flags = s->flags;
172 serve->virtstyle = s->virtstyle;
173 serve->cidrlen = s->cidrlen;
175 if(s->prerun)
176 serve->prerun = g_strdup(s->prerun);
178 if(s->postrun)
179 serve->postrun = g_strdup(s->postrun);
181 if(s->transactionlog)
182 serve->transactionlog = g_strdup(s->transactionlog);
184 if(s->servename)
185 serve->servename = g_strdup(s->servename);
187 if(s->cowdir)
188 serve->cowdir = g_strdup(s->cowdir);
190 serve->max_connections = s->max_connections;
192 return serve;
195 uint64_t size_autodetect(int fhandle) {
196 off_t es;
197 u64 bytes __attribute__((unused));
198 struct stat stat_buf;
199 int error;
201 #ifdef HAVE_SYS_MOUNT_H
202 #ifdef HAVE_SYS_IOCTL_H
203 #ifdef BLKGETSIZE64
204 DEBUG("looking for export size with ioctl BLKGETSIZE64\n");
205 if (!ioctl(fhandle, BLKGETSIZE64, &bytes) && bytes) {
206 return bytes;
208 #endif /* BLKGETSIZE64 */
209 #endif /* HAVE_SYS_IOCTL_H */
210 #endif /* HAVE_SYS_MOUNT_H */
212 DEBUG("looking for fhandle size with fstat\n");
213 stat_buf.st_size = 0;
214 error = fstat(fhandle, &stat_buf);
215 if (!error) {
216 /* always believe stat if a regular file as it might really
217 * be zero length */
218 if (S_ISREG(stat_buf.st_mode) || (stat_buf.st_size > 0))
219 return (uint64_t)stat_buf.st_size;
220 } else {
221 DEBUG("fstat failed: %s", strerror(errno));
224 DEBUG("looking for fhandle size with lseek SEEK_END\n");
225 es = lseek(fhandle, (off_t)0, SEEK_END);
226 if (es > ((off_t)0)) {
227 return (uint64_t)es;
228 } else {
229 DEBUG("lseek failed: %d", errno==EBADF?1:(errno==ESPIPE?2:(errno==EINVAL?3:4)));
232 DEBUG("Could not find size of exported block device: %s", strerror(errno));
233 return UINT64_MAX;
236 int exptrim(struct nbd_request* req, CLIENT* client) {
237 /* Don't trim when we're read only */
238 if(client->server->flags & F_READONLY) {
239 errno = EINVAL;
240 return -1;
242 /* Don't trim beyond the size of the device, please */
243 if(req->from + req->len > client->exportsize) {
244 errno = EINVAL;
245 return -1;
247 /* For copy-on-write, we should trim on the diff file. Not yet
248 * implemented. */
249 if(client->server->flags & F_COPYONWRITE) {
250 DEBUG("TRIM not supported yet on copy-on-write exports");
251 return 0;
253 if (client->server->flags & F_TREEFILES) {
254 /* start address of first block to be trimmed */
255 off_t min = ( ( req->from + TREEPAGESIZE - 1 ) / TREEPAGESIZE) * TREEPAGESIZE;
256 /* start address of first block NOT to be trimmed */
257 off_t max = ( ( req->from + req->len ) / TREEPAGESIZE) * TREEPAGESIZE;
258 while (min<max) {
259 delete_treefile(client->exportname,client->exportsize,min);
260 min+=TREEPAGESIZE;
262 DEBUG("Performed TRIM request on TREE structure from %llu to %llu", (unsigned long long) req->from, (unsigned long long) req->len);
263 return 0;
265 FILE_INFO cur = g_array_index(client->export, FILE_INFO, 0);
266 FILE_INFO next;
267 int i = 1;
268 do {
269 if(i<client->export->len) {
270 next = g_array_index(client->export, FILE_INFO, i);
271 } else {
272 next.fhandle = -1;
273 next.startoff = client->exportsize;
275 if(cur.startoff <= req->from && next.startoff - cur.startoff >= req->len) {
276 off_t reqoff = req->from - cur.startoff;
277 off_t curlen = next.startoff - reqoff;
278 off_t reqlen = curlen - reqoff > req->len ? req->len : curlen - reqoff;
279 punch_hole(cur.fhandle, reqoff, reqlen);
281 cur = next;
282 i++;
283 } while(i < client->export->len && cur.startoff < (req->from + req->len));
284 DEBUG("Performed TRIM request from %llu to %llu", (unsigned long long) req->from, (unsigned long long) req->len);
285 return 0;
288 void myseek(int handle,off_t a) {
289 if (lseek(handle, a, SEEK_SET) < 0) {
290 err("Can not seek locally!\n");