17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <treefiles.h>
21 #ifdef HAVE_SYS_IOCTL_H
22 #include <sys/ioctl.h>
24 #ifdef HAVE_SYS_MOUNT_H
25 #include <sys/mount.h>
28 #define LINELEN 256 /**< Size of static buffer used to read the
29 authorization file (yuck) */
32 bool address_matches(const char* mask, const struct sockaddr* addr, GError** err) {
33 struct addrinfo *res, *aitmp, hints;
35 char privmask[strlen(mask)+1];
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};
42 strcpy(privmask, mask);
44 memset(&hints, 0, sizeof(hints));
45 hints.ai_family = AF_UNSPEC;
46 hints.ai_flags = AI_NUMERICHOST;
48 if((masksep = strchr(privmask, '/'))) {
50 masklen = strtol(++masksep, NULL, 10);
52 masklen = addrlen * 8;
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");
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));
67 assert(addr->sa_family == AF_INET || addr->sa_family == AF_INET6);
68 const uint8_t* byte_s;
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) {
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));
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));
85 /* Addresses are different families, compare IPv4-mapped IPv6 address */
86 switch(addr->sa_family) {
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;
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;
103 while(len_left >= 8) {
104 if(*byte_s != *byte_t) {
111 mask = getmaskbyte(len_left);
112 if((*byte_s & mask) != (*byte_t & mask)) {
125 uint8_t getmaskbyte(int masklen) {
130 for(int i = 7; i + masklen > 7; i--) {
137 int authorized_client(CLIENT *opts) {
141 if (opts->server->authname == NULL) {
142 msg(LOG_INFO, "No authorization file, granting access.");
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));
152 while (fgets(line,LINELEN,f)!=NULL) {
156 if((pos = strchr(line, '#'))) {
159 /* Skip whitespace */
161 while((*pos) && isspace(*pos)) {
164 /* Skip content-free lines */
168 /* Trim trailing whitespace */
170 while ((*endpos) && !isspace(*endpos))
173 if(address_matches(pos, (struct sockaddr*)&opts->clientaddr, NULL)) {
184 * @param s the old server we want to duplicate
185 * @return new duplicated server
187 SERVER* dup_serve(const SERVER *const s) {
188 SERVER *serve = NULL;
190 serve=g_new0(SERVER, 1);
195 serve->exportname = g_strdup(s->exportname);
197 serve->expected_size = s->expected_size;
200 serve->listenaddr = g_strdup(s->listenaddr);
203 serve->authname = g_strdup(s->authname);
205 serve->flags = s->flags;
206 serve->virtstyle = s->virtstyle;
207 serve->cidrlen = s->cidrlen;
210 serve->prerun = g_strdup(s->prerun);
213 serve->postrun = g_strdup(s->postrun);
215 if(s->transactionlog)
216 serve->transactionlog = g_strdup(s->transactionlog);
219 serve->servename = g_strdup(s->servename);
222 serve->cowdir = g_strdup(s->cowdir);
224 serve->max_connections = s->max_connections;
229 uint64_t size_autodetect(int fhandle) {
231 u64 bytes __attribute__((unused));
232 struct stat stat_buf;
235 #ifdef HAVE_SYS_MOUNT_H
236 #ifdef HAVE_SYS_IOCTL_H
238 DEBUG("looking for export size with ioctl BLKGETSIZE64\n");
239 if (!ioctl(fhandle, BLKGETSIZE64, &bytes) && bytes) {
242 #endif /* BLKGETSIZE64 */
243 #endif /* HAVE_SYS_IOCTL_H */
244 #endif /* HAVE_SYS_MOUNT_H */
246 DEBUG("looking for fhandle size with fstat\n");
247 stat_buf.st_size = 0;
248 error = fstat(fhandle, &stat_buf);
250 /* always believe stat if a regular file as it might really
252 if (S_ISREG(stat_buf.st_mode) || (stat_buf.st_size > 0))
253 return (uint64_t)stat_buf.st_size;
255 DEBUG("fstat failed: %s", strerror(errno));
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)) {
263 DEBUG("lseek failed: %d", errno==EBADF?1:(errno==ESPIPE?2:(errno==EINVAL?3:4)));
266 DEBUG("Could not find size of exported block device: %s", strerror(errno));
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
276 if(client->server->flags & F_COPYONWRITE) {
277 DEBUG("TRIM not supported yet on copy-on-write exports");
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;
286 delete_treefile(client->exportname,client->exportsize,min);
289 DEBUG("Performed TRIM request on TREE structure from %llu to %llu", (unsigned long long) req->from, (unsigned long long) req->len);
292 FILE_INFO cur = g_array_index(client->export, FILE_INFO, 0);
296 if(i<client->export->len) {
297 next = g_array_index(client->export, FILE_INFO, i);
300 next.startoff = client->exportsize;
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);
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);
315 pthread_mutex_t cntmutex = PTHREAD_MUTEX_INITIALIZER;
317 SERVER* serve_inc_ref(SERVER *s) {
318 pthread_mutex_lock(&cntmutex);
320 pthread_mutex_unlock(&cntmutex);
324 SERVER* serve_dec_ref(SERVER *s) {
325 pthread_mutex_lock(&cntmutex);
326 if(--(s->refcnt) == 0) {
330 pthread_mutex_unlock(&cntmutex);
334 void serve_clear_element(SERVER **server) {
335 serve_dec_ref(*server);