61 #define _XOPEN_SOURCE 500
63 #define _DARWIN_C_SOURCE
66 #include <sys/types.h>
67 #include <sys/socket.h>
69 #include <sys/select.h>
72 #ifdef HAVE_SYS_IOCTL_H
73 #include <sys/ioctl.h>
75 #include <sys/param.h>
79 #include <netinet/tcp.h>
80 #include <netinet/in.h>
90 #include <linux/falloc.h>
92 #include <arpa/inet.h>
98 #ifdef HAVE_SYS_DIRENT_H
99 #include <sys/dirent.h>
107 #include <inttypes.h>
112 #define MY_NAME "nbd_server"
120 #include <sdp_inet.h>
123 #if HAVE_FSCTL_SET_ZERO_DATA
129 #include <winioctl.h>
134 #define SYSCONFDIR "/etc"
136 #define CFILE SYSCONFDIR "/nbd-server/config"
151 #define OFFT_MAX ~((off_t)1<<(sizeof(off_t)*8-1))
152 #define BUFSIZE ((1024*1024)+sizeof(struct nbd_reply))
153 #define DIFFPAGESIZE 4096
158 #define F_NO_ZEROES 4
163 #define NEG_INIT (1 << 0)
164 #define NEG_OLD (1 << 1)
165 #define NEG_MODERN (1 << 2)
255 return "NBD_CMD_READ";
257 return "NBD_CMD_WRITE";
259 return "NBD_CMD_DISC";
261 return "NBD_CMD_FLUSH";
263 return "NBD_CMD_TRIM";
277 static inline void consume(
int f,
void * buf,
size_t len,
size_t bufsiz) {
280 curlen = (len>bufsiz)?bufsiz:len;
297 if ((res = write(f, buf, len)) <= 0)
298 err(
"Send failed: %m");
309 printf(
"This is nbd-server version " VERSION "\n");
310 printf(
"Usage: [ip:|ip6@]port file_to_export [size][kKmM] [-l authorize_file] [-r] [-m] [-c] [-C configuration file] [-p PID file name] [-o section name] [-M max connections] [-V]\n"
311 "\t-r|--read-only\t\tread only\n"
312 "\t-m|--multi-file\t\tmultiple file\n"
313 "\t-c|--copy-on-write\tcopy on write\n"
314 "\t-C|--config-file\tspecify an alternate configuration file\n"
315 "\t-l|--authorize-file\tfile with list of hosts that are allowed to\n\t\t\t\tconnect.\n"
316 "\t-p|--pid-file\t\tspecify a filename to write our PID to\n"
317 "\t-o|--output-config\toutput a config file section for what you\n\t\t\t\tspecified on the command line, with the\n\t\t\t\tspecified section name\n"
318 "\t-M|--max-connections\tspecify the maximum number of opened connections\n"
319 "\t-V|--version\toutput the version and exit\n\n"
320 "\tif port is set to 0, stdin is used (for running from inetd).\n"
321 "\tif file_to_export contains '%%s', it is substituted with the IP\n"
322 "\t\taddress of the machine trying to connect\n"
323 "\tif ip is set, it contains the local IP address on which we're listening.\n\tif not, the server will listen on all local IP addresses\n");
324 printf(
"Using configuration file %s\n",
CFILE);
329 printf(
"[%s]\n", section_header);
330 printf(
"\texportname = %s\n", serve->
exportname);
331 printf(
"\tlistenaddr = %s\n", serve->
listenaddr);
333 printf(
"\treadonly = true\n");
336 printf(
"\tmultifile = true\n");
339 printf(
"\ttreefiles = true\n");
342 printf(
"\tcopyonwrite = true\n");
345 printf(
"\tfilesize = %lld\n", (
long long int)serve->
expected_size);
348 printf(
"\tauthfile = %s\n", serve->
authname);
363 struct option long_options[] = {
364 {
"read-only", no_argument, NULL,
'r'},
365 {
"multi-file", no_argument, NULL,
'm'},
366 {
"copy-on-write", no_argument, NULL,
'c'},
367 {
"dont-fork", no_argument, NULL,
'd'},
368 {
"authorize-file", required_argument, NULL,
'l'},
369 {
"config-file", required_argument, NULL,
'C'},
370 {
"pid-file", required_argument, NULL,
'p'},
371 {
"output-config", required_argument, NULL,
'o'},
372 {
"max-connection", required_argument, NULL,
'M'},
373 {
"version", no_argument, NULL,
'V'},
380 gboolean do_output=FALSE;
381 gchar* section_header=
"";
390 while((c=getopt_long(argc, argv,
"-C:cdl:mo:rp:M:V", long_options, &i))>=0) {
394 switch(nonspecial++) {
396 if(strchr(optarg,
':') == strrchr(optarg,
':')) {
397 addr_port=g_strsplit(optarg,
":", 2);
402 g_strfreev(addr_port);
403 addr_port=g_strsplit(optarg,
"@", 2);
406 addr_port=g_strsplit(optarg,
"@", 2);
417 g_strfreev(addr_port);
422 fprintf(stderr,
"E: The to be exported file needs to be an absolute filename!\n");
427 last=strlen(optarg)-1;
429 if (suffix ==
'k' || suffix ==
'K' ||
430 suffix ==
'm' || suffix ==
'M')
432 es = (off_t)atoll(optarg);
452 section_header = g_strdup(optarg);
476 printf(
"This is nbd-server version " VERSION "\n");
495 g_critical(
"Need a complete configuration on the command line to output a config file section!");
506 #ifdef HAVE_STRUCT_DIRENT_D_TYPE
507 #define NBD_D_TYPE de->d_type
520 DIR* dirh = opendir(dir);
523 GArray* retval = NULL;
532 while((de = readdir(dirh))) {
533 int saved_errno=errno;
534 fname = g_build_filename(dir, de->d_name, NULL);
540 if(stat(fname, &stbuf)) {
544 if (!S_ISREG(stbuf.st_mode)) {
549 if(strcmp((de->d_name + strlen(de->d_name) - 5),
".conf")) {
558 retval = g_array_new(FALSE, TRUE,
sizeof(
SERVER));
559 retval = g_array_append_vals(retval, tmp->data, tmp->len);
560 g_array_free(tmp, TRUE);
571 g_array_free(retval, TRUE);
601 const char* DEFAULT_ERROR =
"Could not parse %s in group %s: %s";
602 const char* MISSING_REQUIRED_ERROR =
"Could not find required value %s in group %s: %s";
605 gchar *virtstyle=NULL;
630 const int lp_size=
sizeof(lp)/
sizeof(
PARAM);
644 int p_size=
sizeof(gp)/
sizeof(
PARAM);
647 const char *err_msg=NULL;
664 memcpy(&genconftmp, genconf,
sizeof(
struct generic_conf));
667 cfile = g_key_file_new();
668 retval = g_array_new(FALSE, TRUE,
sizeof(
SERVER));
669 if(!g_key_file_load_from_file(cfile, f, G_KEY_FILE_KEEP_COMMENTS |
670 G_KEY_FILE_KEEP_TRANSLATIONS, &err)) {
673 g_key_file_free(cfile);
676 startgroup = g_key_file_get_start_group(cfile);
677 if((!startgroup || strcmp(startgroup,
"generic")) && expect_generic) {
679 g_key_file_free(cfile);
682 groups = g_key_file_get_groups(cfile, NULL);
683 for(i=0;groups[i];i++) {
684 memset(&s,
'\0',
sizeof(
SERVER));
688 if(i==1 || !expect_generic) {
692 for(j=0;j<p_size;j++) {
693 assert(p[j].target != NULL);
694 assert(p[j].ptype==PARAM_INT||p[j].ptype==PARAM_STRING||p[j].ptype==PARAM_BOOL||p[j].ptype==
PARAM_INT64);
697 ival = g_key_file_get_integer(cfile,
702 *((gint*)p[j].target) = ival;
706 i64val = g_key_file_get_int64(cfile,
711 *((gint64*)p[j].target) = i64val;
715 sval = g_key_file_get_string(cfile,
720 *((gchar**)p[j].target) = sval;
724 bval = g_key_file_get_boolean(cfile,
726 p[j].paramname, &err);
729 *((gint*)p[j].target) |= p[j].flagval;
731 *((gint*)p[j].target) &= ~(p[j].flagval);
737 if(err->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
743 err_msg = MISSING_REQUIRED_ERROR;
746 err_msg = DEFAULT_ERROR;
749 g_array_free(retval, TRUE);
751 g_key_file_free(cfile);
756 if(!strncmp(virtstyle,
"none", 4)) {
758 }
else if(!strncmp(virtstyle,
"ipliteral", 9)) {
760 }
else if(!strncmp(virtstyle,
"iphash", 6)) {
762 }
else if(!strncmp(virtstyle,
"cidrhash", 8)) {
764 if(strlen(virtstyle)<10) {
766 g_array_free(retval, TRUE);
767 g_key_file_free(cfile);
770 s.
cidrlen=strtol(virtstyle+8, NULL, 0);
773 g_array_free(retval, TRUE);
774 g_key_file_free(cfile);
781 g_message(
"Since 3.10, the oldstyle protocol is no longer supported. Please migrate to the newstyle protocol.");
782 g_message(
"Exiting.");
788 if(i>0 || !expect_generic) {
791 g_array_append_val(retval, s);
796 g_array_free(retval, TRUE);
797 g_key_file_free(cfile);
802 g_key_file_free(cfile);
806 retval = g_array_append_vals(retval, extra->data, extra->len);
808 g_array_free(extra, TRUE);
811 g_array_free(retval, TRUE);
816 if(i==1 && expect_generic) {
823 memcpy(genconf, &genconftmp,
sizeof(
struct generic_conf));
850 void killchild(gpointer key, gpointer value, gpointer user_data) {
895 GArray *
const export = client->
export;
912 int end = export->len - 1;
913 while( start <= end ) {
914 int mid = (start + end) / 2;
915 fi = g_array_index(export,
FILE_INFO, mid);
929 fi = g_array_index(export,
FILE_INFO, end);
933 if( end+1 < export->len ) {
958 if(
get_filepos(client, a, &fhandle, &foffset, &maxbytes))
960 if(maxbytes && len > maxbytes)
963 DEBUG(
"(WRITE to fd %d offset %llu len %u fua %d), ", fhandle, (
long long unsigned)foffset, (
unsigned int)len, fua);
965 retval = pwrite(fhandle, buf, len, foffset);
1001 sync_file_range(fhandle, foffset, len,
1002 SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE |
1003 SYNC_FILE_RANGE_WAIT_AFTER);
1028 while(len > 0 && (ret=
rawexpwrite(a, buf, len, client, fua)) > 0 ) {
1033 return (ret < 0 || len != 0);
1053 if(
get_filepos(client, a, &fhandle, &foffset, &maxbytes))
1055 if(maxbytes && len > maxbytes)
1058 DEBUG(
"(READ from fd %d offset %llu len %u), ", fhandle, (
long long unsigned int)foffset, (
unsigned int)len);
1060 retval = pread(fhandle, buf, len, foffset);
1074 while(len > 0 && (ret=
rawexpread(a, buf, len, client)) > 0 ) {
1079 return (ret < 0 || len != 0);
1093 off_t rdlen, offset;
1094 off_t mapcnt, mapl, maph, pagestart;
1098 DEBUG(
"Asked to read %u bytes at %llu.\n", (
unsigned int)len, (
unsigned long long)a);
1102 for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
1105 rdlen=(0<DIFFPAGESIZE-offset && len<(size_t)(DIFFPAGESIZE-offset)) ?
1106 len : (
size_t)DIFFPAGESIZE-offset;
1107 if (client->
difmap[mapcnt]!=(u32)(-1)) {
1108 DEBUG(
"Page %llu is at %lu\n", (
unsigned long long)mapcnt,
1109 (
unsigned long)(client->
difmap[mapcnt]));
1111 if (read(client->
difffile, buf, rdlen) != rdlen)
return -1;
1113 DEBUG(
"Page %llu is not here, we read the original one\n",
1114 (
unsigned long long)mapcnt);
1117 len-=rdlen; a+=rdlen; buf+=rdlen;
1136 off_t mapcnt,mapl,maph;
1143 DEBUG(
"Asked to write %u bytes at %llu.\n", (
unsigned int)len, (
unsigned long long)a);
1147 for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
1149 offset=a-pagestart ;
1150 wrlen=(0<DIFFPAGESIZE-offset && len<(size_t)(DIFFPAGESIZE-offset)) ?
1151 len : (
size_t)DIFFPAGESIZE-offset;
1153 if (client->
difmap[mapcnt]!=(u32)(-1)) {
1154 DEBUG(
"Page %llu is at %lu\n", (
unsigned long long)mapcnt,
1155 (
unsigned long)(client->
difmap[mapcnt])) ;
1157 client->
difmap[mapcnt]*DIFFPAGESIZE+offset);
1158 if (write(client->
difffile, buf, wrlen) != wrlen)
return -1 ;
1162 DEBUG(
"Page %llu is not here, we put it at %lu\n",
1163 (
unsigned long long)mapcnt,
1164 (
unsigned long)(client->
difmap[mapcnt]));
1168 memcpy(pagebuf+offset,buf,wrlen) ;
1169 if (write(client->
difffile, pagebuf, DIFFPAGESIZE) !=
1173 len-=wrlen ; a+=wrlen ; buf+=wrlen ;
1207 for (i = 0; i < client->
export->len; i++) {
1217 DEBUG(
"punching hole in fd=%d, starting from %llu, length %llu\n", fd, (
unsigned long long)off, (
unsigned long long)len);
1219 fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, off, len);
1220 #elif HAVE_FSCTL_SET_ZERO_DATA
1221 FILE_ZERO_DATA_INFORMATION zerodata;
1222 zerodata.FileOffset.QuadPart = off;
1223 zerodata.BeyondFinalZero.QuadPart = off +
len;
1224 HANDLE w32handle = (HANDLE)_get_osfhandle(fd);
1226 DeviceIoControl(w32handle, FSCTL_SET_ZERO_DATA, &zerodata,
sizeof(zerodata), NULL, 0, &bytesret, NULL);
1228 DEBUG(
"punching holes not supported on this platform\n");
1232 static void send_reply(uint32_t opt,
int net, uint32_t reply_type,
size_t datasize,
void* data) {
1234 reply_type = htonl(reply_type);
1235 uint32_t datsize = htonl(datasize);
1237 struct iovec v_data[] = {
1239 { &opt,
sizeof(opt) },
1240 { &reply_type,
sizeof(reply_type) },
1241 { &datsize,
sizeof(datsize) },
1244 size_t total =
sizeof(
magic) +
sizeof(opt) +
sizeof(reply_type) +
sizeof(datsize) + datasize;
1245 ssize_t sent = writev(net, v_data, 5);
1247 perror(
"E: couldn't write enough data:");
1256 if (read(net, &namelen,
sizeof(namelen)) < 0) {
1257 err(
"Negotiation failed/7: %m");
1260 namelen = ntohl(namelen);
1262 name = malloc(namelen+1);
1264 if (read(net, name, namelen) < 0) {
1265 err(
"Negotiation failed/8: %m");
1272 for(i=0; i<servers->len; i++) {
1282 pthread_mutex_init(&(client->
lock), NULL);
1287 err(
"Negotiation failed/8a: Requested export not found");
1292 static void handle_list(uint32_t opt,
int net, GArray* servers, uint32_t cflags) {
1296 char *ptr = buf +
sizeof(
len);
1298 if (read(net, &len,
sizeof(len)) < 0)
1299 err(
"Negotiation failed/8: %m");
1309 for(i=0; i<servers->len; i++) {
1312 memcpy(buf, &len,
sizeof(len));
1327 uint32_t cflags = 0;
1330 assert(servers != NULL);
1334 if (write(net, &magic,
sizeof(magic)) < 0)
1337 smallflags = htons(smallflags);
1338 if (write(net, &smallflags,
sizeof(uint16_t)) < 0)
1340 if (read(net, &cflags,
sizeof(cflags)) < 0)
1342 cflags = htonl(cflags);
1347 if (read(net, &magic,
sizeof(magic)) < 0)
1354 if (read(net, &opt,
sizeof(opt)) < 0)
1379 err_nonfatal(
"Weird things happened: reached end of negotiation without success");
1387 if (write(client->
net, &size_host, 8) < 0)
1388 err(
"Negotiation failed/9: %m");
1399 flags = htons(flags);
1400 if (write(client->
net, &flags,
sizeof(flags)) < 0)
1401 err(
"Negotiation failed/11: %m");
1404 memset(zeros,
'\0',
sizeof(zeros));
1405 if (write(client->
net, zeros, 124) < 0)
1406 err(
"Negotiation failed/12: %m");
1432 g_free(package->
data);
1433 g_free(package->
req);
1457 void* buf = malloc(req->
len);
1459 err(
"Could not allocate memory for request");
1461 DEBUG(
"handling read request\n");
1464 DEBUG(
"Read failed: %m");
1467 pthread_mutex_lock(&(client->
lock));
1470 pthread_mutex_unlock(&(client->
lock));
1476 DEBUG(
"handling write request\n");
1481 DEBUG(
"[WRITE to READONLY!]");
1485 DEBUG(
"Write failed: %m");
1489 pthread_mutex_lock(&(client->
lock));
1491 pthread_mutex_unlock(&(client->
lock));
1496 DEBUG(
"handling flush request\n");
1499 DEBUG(
"Flush failed: %m");
1502 pthread_mutex_lock(&(client->
lock));
1504 pthread_mutex_unlock(&(client->
lock));
1509 DEBUG(
"handling trim request\n");
1512 DEBUG(
"Trim failed: %m");
1515 pthread_mutex_lock(&(client->
lock));
1517 pthread_mutex_unlock(&(client->
lock));
1521 struct work_package*
package = (struct work_package*) data;
1528 handle_write(package->client, package->req, package->data);
1537 msg(LOG_ERR,
"E: received unknown command %d of type, ignoring", package->req->type);
1541 pthread_mutex_lock(&(package->client->lock));
1542 writeit(package->client->net, &rep,
sizeof rep);
1543 pthread_mutex_unlock(&(package->client->lock));
1555 DEBUG(
"Entering request loop\n");
1566 req->
len = ntohl(req->
len);
1569 err(
"Protocol error: not enough magic.");
1577 g_thread_pool_free(
tpool, FALSE, TRUE);
1580 g_thread_pool_push(
tpool, pkg, NULL);
1585 #define SEND(net,reply) { writeit( net, &reply, sizeof( reply )); \
1586 if (client->transactionlogfd != -1) \
1587 writeit(client->transactionlogfd, &reply, sizeof(reply)); }
1589 #define ERROR(client,reply,errcode) { reply.error = nbd_errno(errcode); SEND(client->net,reply); reply.error = 0; }
1602 gboolean go_on=TRUE;
1607 DEBUG(
"Entering request loop!\n");
1621 readit(client->
net, &request,
sizeof(request));
1626 request.
type = ntohl(request.
type);
1628 len = ntohl(request.
len);
1631 (
unsigned long long)request.
from,
1632 (
unsigned long long)request.
from / 512, len);
1635 err(
"Not enough magic.");
1641 if (request.
from + len < request.
from) {
1642 DEBUG(
"[Number too large!]");
1643 ERROR(client, reply, EINVAL);
1657 msg(LOG_DEBUG,
"oversized request (this is not a problem)");
1666 msg(LOG_INFO,
"Disconnect request received.");
1677 DEBUG(
"wr: net->buf, ");
1680 DEBUG(
"buf->exp, ");
1683 DEBUG(
"[WRITE to READONLY!]");
1684 ERROR(client, reply, EPERM);
1690 DEBUG(
"Write failed: %m" );
1691 ERROR(client, reply, errno);
1696 request.
from += currlen;
1706 DEBUG(
"Flush failed: %m");
1707 ERROR(client, reply, errno);
1715 DEBUG(
"exp->buf, ");
1723 DEBUG(
"Read failed: %m");
1724 ERROR(client, reply, errno);
1728 DEBUG(
"buf->net, ");
1731 request.
from += currlen;
1744 DEBUG(
"[TRIM to READONLY!]");
1745 ERROR(client, reply, EPERM);
1748 if (
exptrim(&request, client)) {
1749 DEBUG(
"Trim failed: %m");
1750 ERROR(client, reply, errno);
1757 DEBUG (
"Ignoring unknown command\n");
1771 off_t laststartoff = 0, lastsize = 0;
1789 gchar* error_string;
1795 O_RDONLY : (O_RDWR | (cancreate?O_CREAT:0));
1798 tmpname=g_strdup_printf(
"%s.%d-XXXXXX", client->
exportname, i);
1799 DEBUG(
"Opening %s\n", tmpname );
1800 fi.
fhandle = mkstemp(tmpname);
1803 tmpname=g_strdup_printf(
"%s.%d", client->
exportname, i);
1807 DEBUG(
"Opening %s\n", tmpname );
1808 fi.
fhandle = open(tmpname, mode, 0600);
1809 if(fi.
fhandle == -1 && mode == O_RDWR) {
1811 fi.
fhandle = open(tmpname, O_RDONLY);
1823 if(multifile && i>0)
1825 error_string=g_strdup_printf(
1826 "Could not open exported file %s: %%m",
1835 fi.
startoff = laststartoff + lastsize;
1836 g_array_append_val(client->
export, fi);
1845 if (!lastsize && cancreate) {
1848 err(
"Could not expand file: %m");
1854 if(!multifile || temporary)
1859 client->
exportsize = laststartoff + lastsize;
1865 err(
"Size of exported file is too big\n");
1872 msg(LOG_INFO,
"Size of exported file/device is %llu", (
unsigned long long)client->
exportsize);
1874 msg(LOG_INFO,
"Total number of files: %d", i);
1890 export_base = g_strdup(basename(client->
exportname));
1894 g_free(export_base);
1895 msg(LOG_INFO,
"About to create map and diff file %s", client->
difffilename) ;
1897 if (client->
difffile<0)
err(
"Could not create diff file (%m)") ;
1899 err(
"Could not allocate memory") ;
1916 if(command && *command) {
1917 cmd = g_strdup_printf(command, file);
1937 S_IRUSR | S_IWUSR)))
1938 g_warning(
"Could not open transaction log %s",
1976 struct sockaddr_storage netaddr;
1977 struct sockaddr* addr = (
struct sockaddr*)&netaddr;
1978 socklen_t addrinlen =
sizeof(
struct sockaddr_storage );
1979 struct addrinfo hints;
1980 struct addrinfo *ai = NULL;
1981 char peername[NI_MAXHOST];
1982 char netname[NI_MAXHOST];
1987 if (getpeername(net, (
struct sockaddr *) &(client->
clientaddr), &addrinlen) < 0) {
1988 msg(LOG_INFO,
"getpeername failed: %m");
1992 if(client->
clientaddr.ss_family == AF_UNIX) {
1993 strcpy(peername,
"unix");
1995 if((e = getnameinfo((
struct sockaddr *)&(client->
clientaddr), addrinlen,
1996 peername, sizeof (peername), NULL, 0, NI_NUMERICHOST))) {
1997 msg(LOG_INFO,
"getnameinfo failed: %s", gai_strerror(e));
2001 memset(&hints,
'\0',
sizeof (hints));
2002 hints.ai_flags = AI_ADDRCONFIG;
2003 e = getaddrinfo(peername, NULL, &hints, &ai);
2006 msg(LOG_INFO,
"getaddrinfo failed: %s", gai_strerror(e));
2012 if(strncmp(peername,
"::ffff:", 7) == 0) {
2013 memmove(peername, peername+7, strlen(peername));
2018 msg(LOG_DEBUG,
"virtualization is off");
2022 msg(LOG_DEBUG,
"virtstyle iphash");
2023 for(i=0;i<strlen(peername);i++) {
2024 if(peername[i]==
'.') {
2029 msg(LOG_DEBUG,
"virtstyle ipliteral");
2034 memcpy(&netaddr, &(client->
clientaddr), addrinlen);
2036 if(client->
clientaddr.ss_family == AF_UNIX) {
2037 tmp = g_strdup(peername);
2039 assert((ai->ai_family == AF_INET) || (ai->ai_family == AF_INET6));
2040 if(ai->ai_family == AF_INET) {
2042 }
else if(ai->ai_family == AF_INET6) {
2045 uint8_t* addrptr = (uint8_t*)(((
struct sockaddr*)&netaddr)->sa_data);
2046 for(
int i = 0; i < addrbits; i+=8) {
2048 masklen = masklen > 0 ? masklen : 0;
2053 getnameinfo((
struct sockaddr *) &netaddr, addrinlen,
2054 netname,
sizeof (netname), NULL, 0, NI_NUMERICHOST);
2055 tmp=g_strdup_printf(
"%s/%s", netname, peername);
2067 msg(LOG_INFO,
"connect from %s, assigned file is %s",
2088 sigemptyset(&newset);
2089 sigaddset(&newset, SIGCHLD);
2090 sigaddset(&newset, SIGTERM);
2091 sigprocmask(SIG_BLOCK, &newset, &oldset);
2094 msg(LOG_ERR,
"Could not fork (%s)", strerror(errno));
2100 pidp = g_malloc(
sizeof(pid_t));
2102 g_hash_table_insert(
children, pidp, pidp);
2108 signal(SIGCHLD, SIG_DFL);
2109 signal(SIGTERM, SIG_DFL);
2110 signal(SIGHUP, SIG_DFL);
2111 sigemptyset(&oldset);
2113 sigprocmask(SIG_SETMASK, &oldset, NULL);
2120 struct sockaddr_storage addrin;
2121 socklen_t addrinlen =
sizeof(addrin);
2124 net = accept(sock, (
struct sockaddr *) &addrin, &addrinlen);
2126 err_nonfatal(
"Failed to accept socket connection: %m");
2149 msg(LOG_INFO,
"Spawned a child process");
2151 msg(LOG_ERR,
"Failed to spawn a child process");
2160 msg(LOG_ERR,
"Modern initial negotiation failed");
2166 msg(LOG_ERR,
"Max connections (%d) reached",
2171 sock_flags_old = fcntl(net, F_GETFL, 0);
2172 if (sock_flags_old == -1) {
2173 msg(LOG_ERR,
"Failed to get socket flags");
2177 sock_flags_new = sock_flags_old & ~O_NONBLOCK;
2178 if (sock_flags_new != sock_flags_old &&
2179 fcntl(net, F_SETFL, sock_flags_new) == -1) {
2180 msg(LOG_ERR,
"Failed to set socket to blocking mode");
2185 msg(LOG_ERR,
"Failed to set peername");
2190 msg(LOG_INFO,
"Client '%s' is not authorized to access",
2217 g_array_free(servers, FALSE);
2220 msg(LOG_INFO,
"Starting to serve");
2243 const GArray *
const servers) {
2246 for (i = 0; i < servers->len; ++i) {
2247 const SERVER server = g_array_index(servers,
SERVER, i);
2249 if (strcmp(servename, server.
servename) == 0)
2268 GArray *new_servers;
2269 const int old_len = servers->len;
2274 g_thread_pool_set_max_threads(
tpool, genconf.
threads, NULL);
2278 for (i = 0; i < new_servers->len; ++i) {
2279 SERVER new_server = g_array_index(new_servers,
SERVER, i);
2284 g_array_append_val(servers, new_server);
2288 retval = servers->len - old_len;
2290 g_array_free(new_servers, TRUE);
2303 sigset_t blocking_mask;
2304 sigset_t original_mask;
2317 FD_SET(sock, &mset);
2318 max=sock>max?sock:max;
2324 if (sigemptyset(&blocking_mask) == -1)
2325 err(
"failed to initialize blocking_mask: %m");
2327 if (sigaddset(&blocking_mask, SIGCHLD) == -1)
2328 err(
"failed to add SIGCHLD to blocking_mask: %m");
2330 if (sigaddset(&blocking_mask, SIGHUP) == -1)
2331 err(
"failed to add SIGHUP to blocking_mask: %m");
2333 if (sigaddset(&blocking_mask, SIGTERM) == -1)
2334 err(
"failed to add SIGTERM to blocking_mask: %m");
2336 if (sigprocmask(SIG_BLOCK, &blocking_mask, &original_mask) == -1)
2337 err(
"failed to block signals: %m");
2356 while ((pid=waitpid(-1, &status, WNOHANG)) > 0) {
2357 if (WIFEXITED(status)) {
2358 msg(LOG_INFO,
"Child exited with %d", WEXITSTATUS(status));
2360 i = g_hash_table_lookup(
children, &pid);
2362 msg(LOG_INFO,
"SIGCHLD received for an unknown child with PID %ld", (
long)pid);
2364 DEBUG(
"Removing %d from the list of children", pid);
2365 g_hash_table_remove(
children, &pid);
2378 GError *gerror = NULL;
2380 msg(LOG_INFO,
"reconfiguration request received");
2386 msg(LOG_ERR,
"failed to append new servers: %s",
2389 for (i = servers->len - n; i < servers->
len; ++i) {
2390 const SERVER server = g_array_index(servers,
2393 msg(LOG_INFO,
"reconfigured new server: %s",
2398 memcpy(&rset, &mset,
sizeof(fd_set));
2399 if (pselect(max + 1, &rset, NULL, NULL, NULL, &original_mask) > 0) {
2403 if(!FD_ISSET(sock, &rset)) {
2434 if (setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,
sizeof(
int)) == -1) {
2436 "failed to set socket option SO_REUSEADDR: %s",
2442 if (setsockopt(socket,SOL_SOCKET,SO_LINGER,&l,
sizeof(l)) == -1) {
2444 "failed to set socket option SO_LINGER: %s",
2448 if (setsockopt(socket,SOL_SOCKET,SO_KEEPALIVE,&yes,
sizeof(
int)) == -1) {
2450 "failed to set socket option SO_KEEPALIVE: %s",
2458 int open_unix(
const gchar *
const sockname, GError **
const gerror) {
2459 struct sockaddr_un sa;
2463 memset(&sa, 0,
sizeof(
struct sockaddr_un));
2464 sa.sun_family = AF_UNIX;
2465 strncpy(sa.sun_path, sockname, 107);
2466 sock = socket(AF_UNIX, SOCK_STREAM, 0);
2469 "failed to open a unix socket: "
2470 "failed to create socket: %s",
2474 if(bind(sock, (
struct sockaddr*)&sa,
sizeof(
struct sockaddr_un))<0) {
2476 "failed to open a unix socket: "
2477 "failed to bind to address %s: %s",
2478 sockname, strerror(errno));
2481 if(listen(sock, 10)<0) {
2483 "failed to open a unix socket: "
2484 "failed to start listening: %s",
2491 if(retval<0 && sock >= 0) {
2499 GError **
const gerror) {
2500 struct addrinfo hints;
2501 struct addrinfo* ai = NULL;
2502 struct addrinfo* ai_bak = NULL;
2508 gchar
const* l_addr = addr;
2510 if(!addr || strlen(addr) == 0) {
2511 l_addr =
"::, 0.0.0.0";
2514 addrs = g_strsplit_set(l_addr,
", \t", -1);
2516 for(
int i=0; addrs[i]!=NULL; i++) {
2517 if(addrs[i][0] ==
'\0') {
2520 memset(&hints,
'\0',
sizeof(hints));
2521 hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
2522 hints.ai_socktype = SOCK_STREAM;
2523 hints.ai_family = AF_UNSPEC;
2524 hints.ai_protocol = IPPROTO_TCP;
2527 if(e != 0 && addrs[i+1] == NULL &&
modernsocks->len == 0) {
2529 "failed to open a modern socket: "
2530 "failed to get address info: %s",
2538 if((sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol))<0) {
2540 "failed to open a modern socket: "
2541 "failed to create a socket: %s",
2547 g_prefix_error(gerror,
"failed to open a modern socket: ");
2551 if(bind(sock, ai->ai_addr, ai->ai_addrlen)) {
2567 "failed to open a modern socket: "
2568 "failed to bind an address to a socket: %s",
2573 if(listen(sock, 10) <0) {
2575 "failed to open a modern socket: "
2576 "failed to start listening on a socket: %s",
2585 freeaddrinfo(ai_bak);
2593 if (retval == -1 && sock >= 0) {
2597 freeaddrinfo(ai_bak);
2606 const gchar *
const modernport,
const gchar* unixsock) {
2607 struct sigaction sa;
2609 GError *gerror = NULL;
2610 if (
open_modern(modernaddr, modernport, &gerror) == -1) {
2611 msg(LOG_ERR,
"failed to setup servers: %s",
2613 g_clear_error(&gerror);
2616 if(unixsock != NULL) {
2617 GError* gerror = NULL;
2618 if(
open_unix(unixsock, &gerror) == -1) {
2619 msg(LOG_ERR,
"failed to setup servers: %s",
2621 g_clear_error(&gerror);
2628 sigemptyset(&sa.sa_mask);
2629 sigaddset(&sa.sa_mask, SIGTERM);
2630 sa.sa_flags = SA_RESTART;
2631 if(sigaction(SIGCHLD, &sa, NULL) == -1)
2632 err(
"sigaction: %m");
2635 sigemptyset(&sa.sa_mask);
2636 sigaddset(&sa.sa_mask, SIGCHLD);
2637 sa.sa_flags = SA_RESTART;
2638 if(sigaction(SIGTERM, &sa, NULL) == -1)
2639 err(
"sigaction: %m");
2642 sigemptyset(&sa.sa_mask);
2643 sa.sa_flags = SA_RESTART;
2644 if(sigaction(SIGHUP, &sa, NULL) == -1)
2645 err(
"sigaction: %m");
2655 #if !defined(NODAEMON)
2663 strncpy(
pidfname,
"/var/run/nbd-server.pid", 255);
2667 fprintf(pidf,
"%d\n", (
int)getpid());
2671 fprintf(stderr,
"Not fatal; continuing");
2675 #define daemonize(serve)
2686 void dousers(
const gchar *
const username,
const gchar *
const groupname) {
2691 gr = getgrnam(groupname);
2693 str = g_strdup_printf(
"Invalid group name: %s", groupname);
2696 if(setgid(gr->gr_gid)<0) {
2697 err(
"Could not set GID: %m");
2701 pw = getpwnam(username);
2703 str = g_strdup_printf(
"Invalid user name: %s", username);
2706 if(setuid(pw->pw_uid)<0) {
2707 err(
"Could not set UID: %m");
2714 GLogLevelFlags log_level,
2715 const gchar *message,
2718 int level=LOG_DEBUG;
2722 case G_LOG_FLAG_FATAL:
2723 case G_LOG_LEVEL_CRITICAL:
2724 case G_LOG_LEVEL_ERROR:
2727 case G_LOG_LEVEL_WARNING:
2730 case G_LOG_LEVEL_MESSAGE:
2731 case G_LOG_LEVEL_INFO:
2734 case G_LOG_LEVEL_DEBUG:
2740 syslog(level,
"%s", message);
2756 fprintf(stderr,
"Bad size of structure. Alignment problems?\n");
2757 exit(EXIT_FAILURE) ;
2760 modernsocks = g_array_new(FALSE, FALSE,
sizeof(
int));
2764 serve=
cmdline(argc, argv, &genconf);
2774 g_array_append_val(servers, *serve);
2778 err(
"inetd mode requires syslog");
2792 if(!servers || !servers->len) {
2793 if(gerr && !(gerr->domain ==
NBDS_ERR
2795 g_warning(
"Could not parse config file: %s",
2796 gerr ? gerr->message :
"Unknown error");
2800 g_warning(
"Specifying an export on the command line no longer uses the oldstyle protocol.");
2803 if((!serve) && (!servers||!servers->len)) {
2805 g_message(
"No configured exports; quitting.");
#define F_TEMPORARY
Whether the backing file is temporary and should be created then unlinked.
The (required) group "generic" is missing.
int expread(off_t a, char *buf, size_t len, CLIENT *client)
Read an amount of bytes at a given offset from the right file.
static void consume(int f, void *buf, size_t len, size_t bufsiz)
Consume data from an FD that we don't want.
int get_filepos(CLIENT *client, off_t a, int *fhandle, off_t *foffset, size_t *maxbytes)
Get the file handle and offset, given an export offset.
This parameter is a string.
gchar * servename
name of the export as selected by nbd-client
gint flagval
Flag mask for this parameter in case ptype is PARAM_BOOL.
void glib_message_syslog_redirect(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
static void handle_trim(CLIENT *client, struct nbd_request *req)
PARAM_TYPE ptype
Type of the parameter.
GArray * export
array of FILE_INFO of exported files; array size is always 1 unless we're doing the multiple file opt...
static void handle_request(gpointer data, gpointer user_data)
Variables associated with a server.
uint8_t getmaskbyte(int masklen)
Gets a byte to allow for address masking.
void destroy_pid_t(gpointer data)
Destroy a pid_t*.
void setup_servers(GArray *const servers, const gchar *const modernaddr, const gchar *const modernport, const gchar *unixsock)
Connect our servers.
void usage()
Print out a message about how to use nbd-server.
uint32_t difffilelen
number of pages in difffile
#define NBD_FLAG_SEND_FUA
#define SEND(net, reply)
sending macro.
#define NBD_FLAG_C_NO_ZEROES
#define NBD_FLAG_NO_ZEROES
int rawexpread_fully(off_t a, char *buf, size_t len, CLIENT *client)
Call rawexpread repeatedly until all data has been read.
int glob_flags
global flags
gchar * config_file_pos
Where our config file actually is.
#define SYSCONFDIR
Default position of the config file.
SERVER * server
The server this client is getting data from.
int copyonwrite_prepare(CLIENT *client)
Failed to set SO_LINGER to a socket.
gchar * postrun
command that will be ran after the client disconnects
int clientfeats
Features supported by this client.
int rawexpwrite_fully(off_t a, char *buf, size_t len, CLIENT *client, int fua)
Call rawexpwrite repeatedly until all data has been written.
gchar * paramname
Name of the parameter, as it appears in the config file.
void dump_section(SERVER *serve, gchar *section_header)
int exptrim(struct nbd_request *req, CLIENT *client)
Punch a hole in the backend file (if supported by the current system).
#define F_AUTOREADONLY
flag to tell us a file is set to autoreadonly
static int socket_accept(const int sock)
This parameter is a boolean.
Failed to bind an address to socket.
void serveloop(GArray *servers)
Loop through the available servers, and serve them.
#define msg(prio,...)
Logging macros.
static void handle_modern_connection(GArray *const servers, const int sock)
Error occurred during readdir()
int fhandle
file descriptor
int dosockopts(const int socket, GError **const gerror)
Set server socket options.
static int get_index_by_servename(const gchar *const servename, const GArray *const servers)
Return the index of the server whose servename matches the given name.
gchar * modernport
port of the modern socket
gchar * user
user we run the server as
#define NBD_REP_ERR_INVALID
#define F_COPYONWRITE
flag to tell us a file is exported using copyonwrite
int open_unix(const gchar *const sockname, GError **const gerror)
off_t startoff
starting offset of this file
The configuration file is not found.
#define F_FLUSH
Whether server wants FLUSH to be sent by the client.
void * data
for read requests
#define NBD_REP_ERR_POLICY
static pid_t spawn_child()
static volatile sig_atomic_t is_sigchld_caught
Flag set by SIGCHLD handler to mark a child exit.
gchar * cowdir
directory for copy-on-write diff files.
static int append_new_servers(GArray *const servers, GError **const gerror)
Parse configuration files and add servers to the array if they don't already exist there...
int expflush(CLIENT *client)
Flush data to a client.
#define F_READONLY
Per-export flags:
struct sockaddr_storage clientaddr
peer, in binary format, network byte order
int flags
flags associated with this exported file
This parameter is an integer.
#define F_LIST
Allow clients to list the exports on a server.
void serveconnection(CLIENT *client)
Serve a connection.
#define TREEPAGESIZE
tree (block) files uses those chunks
GArray * modernsocks
Sockets for the modern handler.
#define F_TREEFILES
flag to tell us a file is exported using -t
#define F_SPARSE
flag to tell us copyronwrite should use a sparse file
void killchild(gpointer key, gpointer value, gpointer user_data)
Kill a child.
gchar * exportname
(unprocessed) filename of the file we're exporting
int net
The actual client socket.
#define NBD_FLAG_SEND_FLUSH
static void sigchld_handler(const int s G_GNUC_UNUSED)
Handle SIGCHLD by setting atomically a flag which will be evaluated in the main loop of the root serv...
gpointer target
Pointer to where the data of this parameter should be written.
int do_run(gchar *command, gchar *file)
Run a command.
static volatile sig_atomic_t is_sigterm_caught
Flag set by SIGTERM handler to mark a exit request.
static int mainloop_threaded(CLIENT *client)
#define NBD_OPT_EXPORT_NAME
void setmysockopt(int sock)
int open_modern(const gchar *const addr, const gchar *const port, GError **const gerror)
gboolean required
Whether this is a required (as opposed to optional) parameter.
void err_nonfatal(const char *s)
gchar * transactionlog
filename for transaction log
Every subnet in its own directory.
gchar * listenaddr
The IP address we're listening on.
#define F_OLDSTYLE
Global flags:
Failed to set SO_KEEPALIVE to a socket.
CLIENT * negotiate(int net, GArray *servers)
Do the initial negotiation.
char * clientname
peer, in human-readable format
static void send_reply(uint32_t opt, int net, uint32_t reply_type, size_t datasize, void *data)
static const char * getcommandname(uint64_t command)
Translate a command name into human readable form.
GArray * do_cfile_dir(gchar *dir, struct generic_conf *const genconf, GError **e)
Parse config file snippets in a directory.
#define F_SDP
flag to tell us the export should be done using the Socket Direct Protocol for RDMA ...
Variables associated with a client connection.
gchar * modernaddr
address of the modern socket
bool logged_oversized
whether we logged oversized requests already
int difffile
filedescriptor of copyonwrite file.
#define OFFT_MAX
The highest value a variable of type off_t can reach.
int set_peername(int net, CLIENT *client)
Find the name of the file we have to serve.
#define NBD_FLAG_HAS_FLAGS
struct work_package * package_create(CLIENT *client, struct nbd_request *req)
void punch_hole(int fd, off_t off, off_t len)
char pidfname[256]
name of our PID file
Failed to get address info.
static void setup_reply(struct nbd_reply *rep, struct nbd_request *req)
Variables associated with an open file.
void myseek(int handle, off_t a)
seek to a position in a file, with error handling.
uint32_t * difmap
see comment on the global difmap for this one
static void sighup_handler(const int s G_GNUC_UNUSED)
Handle SIGHUP by setting atomically a flag which will be evaluated in the main loop of the root serve...
static void handle_list(uint32_t opt, int net, GArray *servers, uint32_t cflags)
static void handle_flush(CLIENT *client, struct nbd_request *req)
gint threads
maximum number of parallel threads we want to run
static CLIENT * handle_export_name(uint32_t opt, int net, GArray *servers, uint32_t cflags)
uint64_t size_autodetect(int fhandle)
Detect the size of a file.
static void package_dispose(struct work_package *package)
SERVER * cmdline(int argc, char *argv[], struct generic_conf *genconf)
Parse the command line.
#define F_ROTATIONAL
Whether server wants the client to implement the elevator algorithm.
PARAM_TYPE
Type of configuration file values.
ssize_t rawexpread(off_t a, char *buf, size_t len, CLIENT *client)
Read an amount of bytes at a given offset from the right file.
void readit(int f, void *buf, size_t len)
Read data from a file descriptor into a buffer.
Literal IP address as part of the filename.
int transactionlogfd
fd for transaction log
int max_connections
maximum number of opened connections
VIRT_STYLE virtstyle
The style of virtualization, if any.
#define NBD_FLAG_READ_ONLY
uint64_t ntohll(uint64_t a)
static volatile sig_atomic_t is_sighup_caught
Flag set by SIGHUP handler to mark a reconfiguration request.
GArray * parse_cfile(gchar *f, struct generic_conf *genconf, bool expect_generic, GError **e)
Parse the config file.
Failed to set SO_REUSEADDR to a socket.
#define F_SYNC
Whether to fsync() after a write.
ssize_t rawexpwrite(off_t a, char *buf, size_t len, CLIENT *client, int fua)
Write an amount of bytes at a given offset to the right file.
#define BUFSIZE
Size of buffer that can hold requests.
#define F_FUA
Whether server wants FUA to be sent by the client.
#define F_MULTIFILE
flag to tell us a file is exported using -m
uint64_t expected_size
size of the exported file as it was told to us through configuration
#define F_TRIM
Whether server wants TRIM (discard) to be sent by the client.
gchar * prerun
command to be ran after connecting a client, but before starting to serve
#define NBD_FLAG_SEND_TRIM
This parameter is an integer.
gchar * unixsock
file name of the unix domain socket
#define NBD_CMD_MASK_COMMAND
gchar * group
group we run running as
uint64_t exportsize
size of the file we're exporting
static int nbd_errno(int errcode)
char * authname
filename of the authorization file
int main(int argc, char *argv[])
Main entry point...
static void writeit(int f, void *buf, size_t len)
Write data from a buffer into a filedescriptor.
static void handle_read(CLIENT *client, struct nbd_request *req)
gboolean modern
client was negotiated using modern negotiation protocol
void setupexport(CLIENT *client)
Set up client export array, which is an array of FILE_INFO.
A config file was specified that does not define any exports.
#define NBD_REQUEST_MAGIC
char * difffilename
filename of the copy-on-write file, if any
void dousers(const gchar *const username, const gchar *const groupname)
Set up user-ID and/or group-ID.
#define NBDS_ERR
Error domain common for all NBD server errors.
char default_authname[]
default name of allow file
#define DIFFPAGESIZE
diff file uses those chunks
Replacing all dots in an ip address by a / before doing the same as in IPLIT.
uint8_t cidrlen
The length of the mask when we use CIDR-style virtualization.
static void sigterm_handler(const int s G_GNUC_UNUSED)
Handle SIGTERM by setting atomically a flag which will be evaluated in the main loop of the root serv...
#define NBD_FLAG_FIXED_NEWSTYLE
A value is not supported in this build.
#define NBD_FLAG_ROTATIONAL
A directory requested does not exist.
static void handle_write(CLIENT *client, struct nbd_request *req, void *data)
int expwrite(off_t a, char *buf, size_t len, CLIENT *client, int fua)
Write an amount of bytes at a given offset to the right file.
char * exportname
(processed) filename of the file we're exporting
#define ERROR(client, reply, errcode)
error macro.
Configuration file values of the "generic" section.
void send_export_info(CLIENT *client)
Configuration file values.
A value is syntactically invalid.
int open_treefile(char *name, mode_t mode, off_t size, off_t pos, pthread_mutex_t *mutex)
int mainloop(CLIENT *client)
Serve a file to a single client.
void logging(const char *name)
#define F_NO_ZEROES
Do not send zeros to client.
void daemonize()
Go daemon (unless we specified at compile time that we didn't want this)
Failed to create a socket.
int authorized_client(CLIENT *opts)
Check whether a client is allowed to connect.
#define NBD_REP_ERR_UNSUP