Revert "Fix memory leak"
[platform/upstream/nbd.git] / nbdsrv.c
1 #include "config.h"
2 #include "nbd-debug.h"
3
4 #include <nbdsrv.h>
5
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>
14 #include <pthread.h>
15
16 #include <sys/stat.h>
17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <treefiles.h>
20 #include "backend.h"
21 #ifdef HAVE_SYS_IOCTL_H
22 #include <sys/ioctl.h>
23 #endif
24 #ifdef HAVE_SYS_MOUNT_H
25 #include <sys/mount.h>
26 #endif
27
28 #define LINELEN 256       /**< Size of static buffer used to read the
29                                authorization file (yuck) */
30 #include <cliserv.h>
31
32 bool address_matches(const char* mask, const struct sockaddr* addr, GError** err) {
33         struct addrinfo *res, *aitmp, hints;
34         char *masksep;
35         char privmask[strlen(mask)+1];
36         int masklen;
37         int addrlen = addr->sa_family == AF_INET ? 4 : 16;
38 #define IPV4_MAP_PREFIX 12
39         uint8_t ipv4_mapped[IPV4_MAP_PREFIX+4] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
40                 255, 255, 0, 0, 0, 0};
41
42         strcpy(privmask, mask);
43
44         memset(&hints, 0, sizeof(hints));
45         hints.ai_family = AF_UNSPEC;
46         hints.ai_flags = AI_NUMERICHOST;
47
48         if((masksep = strchr(privmask, '/'))) {
49                 *masksep = '\0';
50                 masklen = strtol(++masksep, NULL, 10);
51         } else {
52                 masklen = addrlen * 8;
53         }
54
55         if (masklen > addrlen * 8 || masklen < 0) {
56                 g_set_error(err, NBDS_ERR, NBDS_ERR_CFILE_VALUE_INVALID, "could not parse netmask line: invalid mask length");
57                 return false;
58         }
59
60         int e;
61         if((e = getaddrinfo(privmask, NULL, &hints, &res))) {
62                 g_set_error(err, NBDS_ERR, NBDS_ERR_GAI, "could not parse netmask line: %s", gai_strerror(e));
63                 return false;
64         }
65         aitmp = res;
66         while(res) {
67                 assert(addr->sa_family == AF_INET || addr->sa_family == AF_INET6);
68                 const uint8_t* byte_s;
69                 uint8_t* byte_t;
70                 uint8_t mask = 0;
71                 int len_left = masklen;
72                 if(res->ai_family == addr->sa_family) {
73                         /* Both addresses are the same address family so do a simple comparison */
74                         switch(addr->sa_family) {
75                         case AF_INET:
76                                 byte_s = (const uint8_t*)(&(((struct sockaddr_in*)addr)->sin_addr));
77                                 byte_t = (uint8_t*)(&(((struct sockaddr_in*)(res->ai_addr))->sin_addr));
78                                 break;
79                         case AF_INET6:
80                                 byte_s = (const uint8_t*)(&(((struct sockaddr_in6*)addr)->sin6_addr));
81                                 byte_t = (uint8_t*)(&(((struct sockaddr_in6*)(res->ai_addr))->sin6_addr));
82                                 break;
83                         }
84                 } else {
85                         /* Addresses are different families, compare IPv4-mapped IPv6 address */
86                         switch(addr->sa_family) {
87                         case AF_INET:
88                                 /* Map client address to IPv6 for comparison */
89                                 memcpy(&ipv4_mapped[IPV4_MAP_PREFIX], &(((struct sockaddr_in*)addr)->sin_addr), 4);
90                                 byte_s = (const uint8_t*)&ipv4_mapped;
91                                 byte_t = (uint8_t*)(&(((struct sockaddr_in6*)(res->ai_addr))->sin6_addr));
92                                 len_left += IPV4_MAP_PREFIX * 8;
93                                 break;
94                         case AF_INET6:
95                                 byte_s = (const uint8_t*)(&(((struct sockaddr_in6*)addr)->sin6_addr));
96                                 /* Map res to IPv6 for comparison */
97                                 memcpy(&ipv4_mapped[IPV4_MAP_PREFIX], &(((struct sockaddr_in*)(res->ai_addr))->sin_addr), 4);
98                                 byte_t = &ipv4_mapped[0];
99                                 len_left += IPV4_MAP_PREFIX * 8;
100                                 break;
101                         }
102                 }
103                 while(len_left >= 8) {
104                         if(*byte_s != *byte_t) {
105                                 goto next;
106                         }
107                         byte_s++; byte_t++;
108                         len_left -= 8;
109                 }
110                 if(len_left) {
111                         mask = getmaskbyte(len_left);
112                         if((*byte_s & mask) != (*byte_t & mask)) {
113                                 goto  next;
114                         }
115                 }
116                 freeaddrinfo(aitmp);
117                 return true;
118         next:
119                 res = res->ai_next;
120         }
121         freeaddrinfo(aitmp);
122         return false;
123 }
124
125 uint8_t getmaskbyte(int masklen) {
126         if(masklen >= 8) {
127                 return 0xFF;
128         }
129         uint8_t retval = 0;
130         for(int i = 7; i + masklen > 7; i--) {
131                 retval |= 1 << i;
132         }
133
134         return retval;
135 }
136
137 int authorized_client(CLIENT *opts) {
138         FILE *f ;
139         char line[LINELEN];
140
141         if (opts->server->authname == NULL) {
142                 msg(LOG_INFO, "No authorization file, granting access.");
143                 return 1;
144         }
145
146         if ((f=fopen(opts->server->authname,"r"))==NULL) {
147                 msg(LOG_INFO, "Can't open authorization file %s (%s).",
148                     opts->server->authname, strerror(errno));
149                 return 1 ;
150         }
151
152         while (fgets(line,LINELEN,f)!=NULL) {
153                 char* pos;
154                 char* endpos;
155                 /* Drop comments */
156                 if((pos = strchr(line, '#'))) {
157                         *pos = '\0';
158                 }
159                 /* Skip whitespace */
160                 pos = line;
161                 while((*pos) && isspace(*pos)) {
162                         pos++;
163                 }
164                 /* Skip content-free lines */
165                 if(!(*pos)) {
166                         continue;
167                 }
168                 /* Trim trailing whitespace */
169                 endpos = pos;
170                 while ((*endpos) && !isspace(*endpos))
171                         endpos++;
172                 *endpos = '\0';
173                 if(address_matches(pos, (struct sockaddr*)&opts->clientaddr, NULL)) {
174                         fclose(f);
175                         return 1;
176                 }
177         }
178         fclose(f);
179         return 0;
180 }
181
182 /**
183  * duplicate server
184  * @param s the old server we want to duplicate
185  * @return new duplicated server
186  **/
187 SERVER* dup_serve(const SERVER *const s) {
188         SERVER *serve = NULL;
189
190         serve=g_new0(SERVER, 1);
191         if(serve == NULL)
192                 return NULL;
193
194         if(s->exportname)
195                 serve->exportname = g_strdup(s->exportname);
196
197         serve->expected_size = s->expected_size;
198
199         if(s->listenaddr)
200                 serve->listenaddr = g_strdup(s->listenaddr);
201
202         if(s->authname)
203                 serve->authname = g_strdup(s->authname);
204
205         serve->flags = s->flags;
206         serve->virtstyle = s->virtstyle;
207         serve->cidrlen = s->cidrlen;
208
209         if(s->prerun)
210                 serve->prerun = g_strdup(s->prerun);
211
212         if(s->postrun)
213                 serve->postrun = g_strdup(s->postrun);
214
215         if(s->transactionlog)
216                 serve->transactionlog = g_strdup(s->transactionlog);
217
218         if(s->servename)
219                 serve->servename = g_strdup(s->servename);
220
221         if(s->cowdir)
222                 serve->cowdir = g_strdup(s->cowdir);
223
224         serve->max_connections = s->max_connections;
225
226         return serve;
227 }
228
229 uint64_t size_autodetect(int fhandle) {
230         off_t es;
231         u64 bytes __attribute__((unused));
232         struct stat stat_buf;
233         int error;
234
235 #ifdef HAVE_SYS_MOUNT_H
236 #ifdef HAVE_SYS_IOCTL_H
237 #ifdef BLKGETSIZE64
238         DEBUG("looking for export size with ioctl BLKGETSIZE64\n");
239         if (!ioctl(fhandle, BLKGETSIZE64, &bytes) && bytes) {
240                 return bytes;
241         }
242 #endif /* BLKGETSIZE64 */
243 #endif /* HAVE_SYS_IOCTL_H */
244 #endif /* HAVE_SYS_MOUNT_H */
245
246         DEBUG("looking for fhandle size with fstat\n");
247         stat_buf.st_size = 0;
248         error = fstat(fhandle, &stat_buf);
249         if (!error) {
250                 /* always believe stat if a regular file as it might really
251                  * be zero length */
252                 if (S_ISREG(stat_buf.st_mode) || (stat_buf.st_size > 0))
253                         return (uint64_t)stat_buf.st_size;
254         } else {
255                 DEBUG("fstat failed: %s", strerror(errno));
256         }
257
258         DEBUG("looking for fhandle size with lseek SEEK_END\n");
259         es = lseek(fhandle, (off_t)0, SEEK_END);
260         if (es > ((off_t)0)) {
261                 return (uint64_t)es;
262         } else {
263                 DEBUG("lseek failed: %d", errno==EBADF?1:(errno==ESPIPE?2:(errno==EINVAL?3:4)));
264         }
265
266         DEBUG("Could not find size of exported block device: %s", strerror(errno));
267         return UINT64_MAX;
268 }
269
270 int exptrim(struct nbd_request* req, CLIENT* client) {
271         /* Caller did range checking */
272         assert(!(client->server->flags & F_READONLY));
273         assert(req->from + req->len <= client->exportsize);
274         /* For copy-on-write, we should trim on the diff file. Not yet
275          * implemented. */
276         if(client->server->flags & F_COPYONWRITE) {
277                 DEBUG("TRIM not supported yet on copy-on-write exports");
278                 return 0;
279         }
280         if (client->server->flags & F_TREEFILES) {
281                 /* start address of first block to be trimmed */
282                 off_t min = ( ( req->from + TREEPAGESIZE - 1 ) / TREEPAGESIZE) * TREEPAGESIZE;
283                 /* start address of first block NOT to be trimmed */
284                 off_t max = ( ( req->from + req->len ) / TREEPAGESIZE) * TREEPAGESIZE;
285                 while (min<max) {
286                         delete_treefile(client->exportname,client->exportsize,min);
287                         min+=TREEPAGESIZE;
288                 }
289                 DEBUG("Performed TRIM request on TREE structure from %llu to %llu", (unsigned long long) req->from, (unsigned long long) req->len);
290                 return 0;
291         }
292         FILE_INFO cur = g_array_index(client->export, FILE_INFO, 0);
293         FILE_INFO next;
294         int i = 1;
295         do {
296                 if(i<client->export->len) {
297                         next = g_array_index(client->export, FILE_INFO, i);
298                 } else {
299                         next.fhandle = -1;
300                         next.startoff = client->exportsize;
301                 }
302                 if(cur.startoff <= req->from && next.startoff - cur.startoff >= req->len) {
303                         off_t reqoff = req->from - cur.startoff;
304                         off_t curlen = next.startoff - reqoff;
305                         off_t reqlen = curlen - reqoff > req->len ? req->len : curlen - reqoff;
306                         punch_hole(cur.fhandle, reqoff, reqlen);
307                 }
308                 cur = next;
309                 i++;
310         } while(i < client->export->len && cur.startoff < (req->from + req->len));
311         DEBUG("Performed TRIM request from %llu to %llu", (unsigned long long) req->from, (unsigned long long) req->len);
312         return 0;
313 }
314
315 pthread_mutex_t cntmutex = PTHREAD_MUTEX_INITIALIZER;
316
317 SERVER* serve_inc_ref(SERVER *s) {
318         pthread_mutex_lock(&cntmutex);
319         s->refcnt++;
320         pthread_mutex_unlock(&cntmutex);
321         return s;
322 }
323
324 SERVER* serve_dec_ref(SERVER *s) {
325         pthread_mutex_lock(&cntmutex);
326         if(--(s->refcnt) == 0) {
327                 g_free(s);
328                 s = NULL;
329         }
330         pthread_mutex_unlock(&cntmutex);
331         return s;
332 }
333
334 void serve_clear_element(SERVER **server) {
335         serve_dec_ref(*server);
336 }