Network Block Device @PACKAGE_VERSION@

nbdsrv.c

Go to the documentation of this file.
00001 #include "config.h"
00002 #include "nbd-debug.h"
00003 
00004 #include <nbdsrv.h>
00005 
00006 #include <assert.h>
00007 #include <ctype.h>
00008 #include <netdb.h>
00009 #include <stdlib.h>
00010 #include <stdio.h>
00011 #include <string.h>
00012 #include <syslog.h>
00013 #include <unistd.h>
00014 
00015 #include <sys/stat.h>
00016 #include <sys/types.h>
00017 #include <sys/socket.h>
00018 #include <treefiles.h>
00019 #include "backend.h"
00020 #ifdef HAVE_SYS_MOUNT_H
00021 #include <sys/mount.h>
00022 #endif
00023 
00024 #define LINELEN 256       /**< Size of static buffer used to read the
00025                                authorization file (yuck) */
00026 
00027 #include <cliserv.h>
00028 
00029 bool address_matches(const char* mask, const struct sockaddr* addr, GError** err) {
00030         struct addrinfo *res, *aitmp, hints;
00031         char *masksep;
00032         char privmask[strlen(mask)+1];
00033         int masklen;
00034         int addrlen = addr->sa_family == AF_INET ? 4 : 16;
00035 
00036         assert(addr->sa_family == AF_INET || addr->sa_family == AF_INET6);
00037 
00038         strcpy(privmask, mask);
00039 
00040         memset(&hints, 0, sizeof(hints));
00041         hints.ai_family = AF_UNSPEC;
00042         hints.ai_flags = AI_NUMERICHOST;
00043 
00044         if((masksep = strchr(privmask, '/'))) {
00045                 *masksep = '\0';
00046                 masklen = strtol(++masksep, NULL, 10);
00047         } else {
00048                 masklen = addrlen * 8;
00049         }
00050 
00051         int e;
00052         if((e = getaddrinfo(privmask, NULL, &hints, &res))) {
00053                 g_set_error(err, NBDS_ERR, NBDS_ERR_GAI, "could not parse netmask line: %s", gai_strerror(e));
00054                 return false;
00055         }
00056         aitmp = res;
00057         while(res) {
00058                 const uint8_t* byte_s;
00059                 uint8_t* byte_t;
00060                 uint8_t mask = 0;
00061                 int len_left = masklen;
00062 /*
00063   This won't work due to v4/v6 family mismatches. (20160906 draht)
00064 
00065                 if(res->ai_family != addr->sa_family) {
00066                         goto next;
00067                 }
00068 */
00069                 switch(addr->sa_family) {
00070                         case AF_INET:
00071                                 byte_s = (const uint8_t*)(&(((struct sockaddr_in*)addr)->sin_addr));
00072                                 byte_t = (uint8_t*)(&(((struct sockaddr_in*)(res->ai_addr))->sin_addr));
00073                                 break;
00074                         case AF_INET6:
00075                                 byte_s = (const uint8_t*)(&(((struct sockaddr_in6*)addr)->sin6_addr));
00076                                 byte_t = (uint8_t*)(&(((struct sockaddr_in6*)(res->ai_addr))->sin6_addr));
00077                                 break;
00078                 }
00079                 while(len_left >= 8) {
00080                         if(*byte_s != *byte_t) {
00081                                 goto next;
00082                         }
00083                         byte_s++; byte_t++;
00084                         len_left -= 8;
00085                 }
00086                 if(len_left) {
00087                         mask = getmaskbyte(len_left);
00088                         if((*byte_s & mask) != (*byte_t & mask)) {
00089                                 goto  next;
00090                         }
00091                 }
00092                 freeaddrinfo(aitmp);
00093                 return true;
00094         next:
00095                 res = res->ai_next;
00096         }
00097         freeaddrinfo(aitmp);
00098         return false;
00099 }
00100 
00101 uint8_t getmaskbyte(int masklen) {
00102         if(masklen >= 8) {
00103                 return 0xFF;
00104         }
00105         uint8_t retval = 0;
00106         for(int i = 7; i + masklen > 7; i--) {
00107                 retval |= 1 << i;
00108         }
00109 
00110         return retval;
00111 }
00112 
00113 int authorized_client(CLIENT *opts) {
00114         FILE *f ;
00115         char line[LINELEN]; 
00116 
00117         if (opts->server->authname == NULL) {
00118                 msg(LOG_INFO, "No authorization file, granting access.");
00119                 return 1;
00120         }
00121 
00122         if ((f=fopen(opts->server->authname,"r"))==NULL) {
00123                 msg(LOG_INFO, "Can't open authorization file %s (%s).",
00124                     opts->server->authname, strerror(errno));
00125                 return 1 ; 
00126         }
00127   
00128         while (fgets(line,LINELEN,f)!=NULL) {
00129                 char* pos;
00130                 /* Drop comments */
00131                 if((pos = strchr(line, '#'))) {
00132                         *pos = '\0';
00133                 }
00134                 /* Skip whitespace */
00135                 pos = line;
00136                 while((*pos) && isspace(*pos)) {
00137                         pos++;
00138                 }
00139                 /* Skip content-free lines */
00140                 if(!(*pos)) {
00141                         continue;
00142                 }
00143                 if(address_matches(line, (struct sockaddr*)&opts->clientaddr, NULL)) {
00144                         fclose(f);
00145                         return 1;
00146                 }
00147         }
00148         fclose(f);
00149         return 0;
00150 }
00151 
00152 /**
00153  * duplicate server
00154  * @param s the old server we want to duplicate
00155  * @return new duplicated server
00156  **/
00157 SERVER* dup_serve(const SERVER *const s) {
00158         SERVER *serve = NULL;
00159 
00160         serve=g_new0(SERVER, 1);
00161         if(serve == NULL)
00162                 return NULL;
00163 
00164         if(s->exportname)
00165                 serve->exportname = g_strdup(s->exportname);
00166 
00167         serve->expected_size = s->expected_size;
00168 
00169         if(s->listenaddr)
00170                 serve->listenaddr = g_strdup(s->listenaddr);
00171 
00172         if(s->authname)
00173                 serve->authname = g_strdup(s->authname);
00174 
00175         serve->flags = s->flags;
00176         serve->virtstyle = s->virtstyle;
00177         serve->cidrlen = s->cidrlen;
00178 
00179         if(s->prerun)
00180                 serve->prerun = g_strdup(s->prerun);
00181 
00182         if(s->postrun)
00183                 serve->postrun = g_strdup(s->postrun);
00184 
00185         if(s->transactionlog)
00186                 serve->transactionlog = g_strdup(s->transactionlog);
00187         
00188         if(s->servename)
00189                 serve->servename = g_strdup(s->servename);
00190 
00191         if(s->cowdir)
00192                 serve->cowdir = g_strdup(s->cowdir);
00193 
00194         serve->max_connections = s->max_connections;
00195 
00196         return serve;
00197 }
00198 
00199 uint64_t size_autodetect(int fhandle) {
00200         off_t es;
00201         u64 bytes __attribute__((unused));
00202         struct stat stat_buf;
00203         int error;
00204 
00205 #ifdef HAVE_SYS_MOUNT_H
00206 #ifdef HAVE_SYS_IOCTL_H
00207 #ifdef BLKGETSIZE64
00208         DEBUG("looking for export size with ioctl BLKGETSIZE64\n");
00209         if (!ioctl(fhandle, BLKGETSIZE64, &bytes) && bytes) {
00210                 return bytes;
00211         }
00212 #endif /* BLKGETSIZE64 */
00213 #endif /* HAVE_SYS_IOCTL_H */
00214 #endif /* HAVE_SYS_MOUNT_H */
00215 
00216         DEBUG("looking for fhandle size with fstat\n");
00217         stat_buf.st_size = 0;
00218         error = fstat(fhandle, &stat_buf);
00219         if (!error) {
00220                 /* always believe stat if a regular file as it might really
00221                  * be zero length */
00222                 if (S_ISREG(stat_buf.st_mode) || (stat_buf.st_size > 0))
00223                         return (uint64_t)stat_buf.st_size;
00224         } else {
00225                 DEBUG("fstat failed: %s", strerror(errno));
00226         }
00227 
00228         DEBUG("looking for fhandle size with lseek SEEK_END\n");
00229         es = lseek(fhandle, (off_t)0, SEEK_END);
00230         if (es > ((off_t)0)) {
00231                 return (uint64_t)es;
00232         } else {
00233                 DEBUG("lseek failed: %d", errno==EBADF?1:(errno==ESPIPE?2:(errno==EINVAL?3:4)));
00234         }
00235 
00236         DEBUG("Could not find size of exported block device: %s", strerror(errno));
00237         return UINT64_MAX;
00238 }
00239 
00240 int exptrim(struct nbd_request* req, CLIENT* client) {
00241         /* Don't trim when we're read only */
00242         if(client->server->flags & F_READONLY) {
00243                 errno = EINVAL;
00244                 return -1;
00245         }
00246         /* Don't trim beyond the size of the device, please */
00247         if(req->from + req->len > client->exportsize) {
00248                 errno = EINVAL;
00249                 return -1;
00250         }
00251         /* For copy-on-write, we should trim on the diff file. Not yet
00252          * implemented. */
00253         if(client->server->flags & F_COPYONWRITE) {
00254                 DEBUG("TRIM not supported yet on copy-on-write exports");
00255                 return 0;
00256         }
00257         if (client->server->flags & F_TREEFILES) {
00258                 /* start address of first block to be trimmed */
00259                 off_t min = ( ( req->from + TREEPAGESIZE - 1 ) / TREEPAGESIZE) * TREEPAGESIZE;
00260                 /* start address of first block NOT to be trimmed */
00261                 off_t max = ( ( req->from + req->len ) / TREEPAGESIZE) * TREEPAGESIZE;
00262                 while (min<max) {
00263                         delete_treefile(client->exportname,client->exportsize,min);
00264                         min+=TREEPAGESIZE;
00265                 }
00266                 DEBUG("Performed TRIM request on TREE structure from %llu to %llu", (unsigned long long) req->from, (unsigned long long) req->len);
00267                 return 0;
00268         }
00269         FILE_INFO cur = g_array_index(client->export, FILE_INFO, 0);
00270         FILE_INFO next;
00271         int i = 1;
00272         do {
00273                 if(i<client->export->len) {
00274                         next = g_array_index(client->export, FILE_INFO, i);
00275                 } else {
00276                         next.fhandle = -1;
00277                         next.startoff = client->exportsize;
00278                 }
00279                 if(cur.startoff <= req->from && next.startoff - cur.startoff >= req->len) {
00280                         off_t reqoff = req->from - cur.startoff;
00281                         off_t curlen = next.startoff - reqoff;
00282                         off_t reqlen = curlen - reqoff > req->len ? req->len : curlen - reqoff;
00283                         punch_hole(cur.fhandle, reqoff, reqlen);
00284                 }
00285                 cur = next;
00286                 i++;
00287         } while(i < client->export->len && cur.startoff < (req->from + req->len));
00288         DEBUG("Performed TRIM request from %llu to %llu", (unsigned long long) req->from, (unsigned long long) req->len);
00289         return 0;
00290 }
00291 
00292 void myseek(int handle,off_t a) {
00293         if (lseek(handle, a, SEEK_SET) < 0) {
00294                 err("Can not seek locally!\n");
00295         }
00296 }