63 #include <sys/types.h>
64 #include <sys/socket.h>
66 #include <sys/select.h>
68 #ifdef HAVE_SYS_IOCTL_H
69 #include <sys/ioctl.h>
71 #include <sys/param.h>
72 #ifdef HAVE_SYS_MOUNT_H
73 #include <sys/mount.h>
77 #include <netinet/tcp.h>
78 #include <netinet/in.h>
88 #include <linux/falloc.h>
90 #include <arpa/inet.h>
103 #define MY_NAME "nbd_server"
109 #include <sdp_inet.h>
114 #define SYSCONFDIR "/etc"
116 #define CFILE SYSCONFDIR "/nbd-server/config"
131 #define OFFT_MAX ~((off_t)1<<(sizeof(off_t)*8-1))
132 #define BUFSIZE ((1024*1024)+sizeof(struct nbd_reply))
133 #define DIFFPAGESIZE 4096
137 #define F_MULTIFILE 2
138 #define F_COPYONWRITE 4
140 #define F_AUTOREADONLY 8
146 #define F_ROTATIONAL 512
147 #define F_TEMPORARY 1024
159 #define NEG_INIT (1 << 0)
160 #define NEG_OLD (1 << 1)
161 #define NEG_MODERN (1 << 2)
237 return "NBD_CMD_READ";
239 return "NBD_CMD_WRITE";
241 return "NBD_CMD_DISC";
243 return "NBD_CMD_FLUSH";
245 return "NBD_CMD_TRIM";
258 static inline void readit(
int f,
void *buf,
size_t len) {
262 if ((res = read(f, buf, len)) <= 0) {
263 if(errno != EAGAIN) {
264 err(
"Read failed: %m");
281 static inline void consume(
int f,
void * buf,
size_t len,
size_t bufsiz) {
284 curlen = (len>bufsiz)?bufsiz:len;
301 if ((res = write(f, buf, len)) <= 0)
302 err(
"Send failed: %m");
313 printf(
"This is nbd-server version " VERSION "\n");
314 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]\n"
315 "\t-r|--read-only\t\tread only\n"
316 "\t-m|--multi-file\t\tmultiple file\n"
317 "\t-c|--copy-on-write\tcopy on write\n"
318 "\t-C|--config-file\tspecify an alternate configuration file\n"
319 "\t-l|--authorize-file\tfile with list of hosts that are allowed to\n\t\t\t\tconnect.\n"
320 "\t-p|--pid-file\t\tspecify a filename to write our PID to\n"
321 "\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"
322 "\t-M|--max-connections\tspecify the maximum number of opened connections\n\n"
323 "\tif port is set to 0, stdin is used (for running from inetd).\n"
324 "\tif file_to_export contains '%%s', it is substituted with the IP\n"
325 "\t\taddress of the machine trying to connect\n"
326 "\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");
327 printf(
"Using configuration file %s\n",
CFILE);
332 printf(
"[%s]\n", section_header);
333 printf(
"\texportname = %s\n", serve->
exportname);
334 printf(
"\tlistenaddr = %s\n", serve->
listenaddr);
335 printf(
"\tport = %d\n", serve->
port);
337 printf(
"\treadonly = true\n");
340 printf(
"\tmultifile = true\n");
343 printf(
"\tcopyonwrite = true\n");
346 printf(
"\tfilesize = %lld\n", (
long long int)serve->
expected_size);
349 printf(
"\tauthfile = %s\n", serve->
authname);
364 struct option long_options[] = {
365 {
"read-only", no_argument, NULL,
'r'},
366 {
"multi-file", no_argument, NULL,
'm'},
367 {
"copy-on-write", no_argument, NULL,
'c'},
368 {
"dont-fork", no_argument, NULL,
'd'},
369 {
"authorize-file", required_argument, NULL,
'l'},
370 {
"config-file", required_argument, NULL,
'C'},
371 {
"pid-file", required_argument, NULL,
'p'},
372 {
"output-config", required_argument, NULL,
'o'},
373 {
"max-connection", required_argument, NULL,
'M'},
380 gboolean do_output=FALSE;
381 gchar* section_header=
"";
390 while((c=getopt_long(argc, argv,
"-C:cdl:mo:rp:M:", 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);
410 serve->
port=strtol(addr_port[1], NULL, 0);
414 serve->
port=strtol(addr_port[0], NULL, 0);
416 g_strfreev(addr_port);
421 fprintf(stderr,
"E: The to be exported file needs to be an absolute filename!\n");
426 last=strlen(optarg)-1;
428 if (suffix ==
'k' || suffix ==
'K' ||
429 suffix ==
'm' || suffix ==
'M')
431 es = (off_t)atoll(optarg);
451 section_header = g_strdup(optarg);
489 g_critical(
"Need a complete configuration on the command line to output a config file section!");
506 DIR* dirh = opendir(dir);
509 GArray* retval = NULL;
518 while((de = readdir(dirh))) {
519 int saved_errno=errno;
520 fname = g_build_filename(dir, de->d_name, NULL);
526 if(stat(fname, &stbuf)) {
530 if (!S_ISREG(stbuf.st_mode)) {
535 if(strcmp((de->d_name + strlen(de->d_name) - 5),
".conf")) {
544 retval = g_array_new(FALSE, TRUE,
sizeof(
SERVER));
545 retval = g_array_append_vals(retval, tmp->data, tmp->len);
546 g_array_free(tmp, TRUE);
557 g_array_free(retval, TRUE);
584 const char* DEFAULT_ERROR =
"Could not parse %s in group %s: %s";
585 const char* MISSING_REQUIRED_ERROR =
"Could not find required value %s in group %s: %s";
588 gchar *virtstyle=NULL;
612 const int lp_size=
sizeof(lp)/
sizeof(
PARAM);
624 int p_size=
sizeof(gp)/
sizeof(
PARAM);
627 const char *err_msg=NULL;
644 memcpy(&genconftmp, genconf,
sizeof(
struct generic_conf));
647 cfile = g_key_file_new();
648 retval = g_array_new(FALSE, TRUE,
sizeof(
SERVER));
649 if(!g_key_file_load_from_file(cfile, f, G_KEY_FILE_KEEP_COMMENTS |
650 G_KEY_FILE_KEEP_TRANSLATIONS, &err)) {
653 g_key_file_free(cfile);
656 startgroup = g_key_file_get_start_group(cfile);
657 if((!startgroup || strcmp(startgroup,
"generic")) && genconf) {
659 g_key_file_free(cfile);
662 groups = g_key_file_get_groups(cfile, NULL);
663 for(i=0;groups[i];i++) {
664 memset(&s,
'\0',
sizeof(
SERVER));
668 if(i==1 || !genconf) {
675 for(j=0;j<p_size;j++) {
676 assert(p[j].target != NULL);
677 assert(p[j].ptype==PARAM_INT||p[j].ptype==PARAM_STRING||p[j].ptype==PARAM_BOOL||p[j].ptype==
PARAM_INT64);
680 ival = g_key_file_get_integer(cfile,
685 *((gint*)p[j].target) = ival;
689 i64val = g_key_file_get_int64(cfile,
694 *((gint64*)p[j].target) = i64val;
698 sval = g_key_file_get_string(cfile,
703 *((gchar**)p[j].target) = sval;
707 bval = g_key_file_get_boolean(cfile,
709 p[j].paramname, &err);
712 *((gint*)p[j].target) |= p[j].flagval;
714 *((gint*)p[j].target) &= ~(p[j].flagval);
720 if(err->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
726 err_msg = MISSING_REQUIRED_ERROR;
729 err_msg = DEFAULT_ERROR;
732 g_array_free(retval, TRUE);
734 g_key_file_free(cfile);
739 if(!strncmp(virtstyle,
"none", 4)) {
741 }
else if(!strncmp(virtstyle,
"ipliteral", 9)) {
743 }
else if(!strncmp(virtstyle,
"iphash", 6)) {
745 }
else if(!strncmp(virtstyle,
"cidrhash", 8)) {
747 if(strlen(virtstyle)<10) {
749 g_array_free(retval, TRUE);
750 g_key_file_free(cfile);
753 s.
cidrlen=strtol(virtstyle+8, NULL, 0);
756 g_array_free(retval, TRUE);
757 g_key_file_free(cfile);
764 g_warning(
"A port was specified, but oldstyle exports were not requested. This may not do what you expect.");
765 g_warning(
"Please read 'man 5 nbd-server' and search for oldstyle for more info");
770 if(i>0 || !genconf) {
779 g_array_free(retval, TRUE);
780 g_key_file_free(cfile);
785 g_key_file_free(cfile);
789 retval = g_array_append_vals(retval, extra->data, extra->len);
791 g_array_free(extra, TRUE);
794 g_array_free(retval, TRUE);
799 if(i==1 && genconf) {
806 memcpy(genconf, &genconftmp,
sizeof(
struct generic_conf));
822 while((pid=waitpid(-1, &status, WNOHANG)) > 0) {
823 if(WIFEXITED(status)) {
824 msg(LOG_INFO,
"Child exited with %d", WEXITSTATUS(status));
826 i=g_hash_table_lookup(
children, &pid);
828 msg(LOG_INFO,
"SIGCHLD received for an unknown child with PID %ld", (
long)pid);
830 DEBUG(
"Removing %d from the list of children", pid);
831 g_hash_table_remove(
children, &pid);
844 void killchild(gpointer key, gpointer value, gpointer user_data) {
886 int get_filepos(GArray* export, off_t a,
int* fhandle, off_t* foffset,
size_t* maxbytes ) {
894 int end = export->len - 1;
895 while( start <= end ) {
896 int mid = (start + end) / 2;
897 fi = g_array_index(export,
FILE_INFO, mid);
911 fi = g_array_index(export,
FILE_INFO, end);
915 if( end+1 < export->len ) {
930 if (lseek(handle, a, SEEK_SET) < 0) {
931 err(
"Can not seek locally!\n");
954 if(maxbytes && len > maxbytes)
957 DEBUG(
"(WRITE to fd %d offset %llu len %u fua %d), ", fhandle, (
long long unsigned)foffset, (
unsigned int)len, fua);
960 retval = write(fhandle, buf, len);
996 sync_file_range(fhandle, foffset, len,
997 SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE |
998 SYNC_FILE_RANGE_WAIT_AFTER);
1019 while(len > 0 && (ret=
rawexpwrite(a, buf, len, client, fua)) > 0 ) {
1024 return (ret < 0 || len != 0);
1045 if(maxbytes && len > maxbytes)
1048 DEBUG(
"(READ from fd %d offset %llu len %u), ", fhandle, (
long long unsigned int)foffset, (
unsigned int)len);
1050 myseek(fhandle, foffset);
1051 return read(fhandle, buf, len);
1061 while(len > 0 && (ret=
rawexpread(a, buf, len, client)) > 0 ) {
1066 return (ret < 0 || len != 0);
1080 off_t rdlen, offset;
1081 off_t mapcnt, mapl, maph, pagestart;
1085 DEBUG(
"Asked to read %u bytes at %llu.\n", (
unsigned int)len, (
unsigned long long)a);
1089 for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
1092 rdlen=(0<DIFFPAGESIZE-offset && len<(size_t)(DIFFPAGESIZE-offset)) ?
1093 len : (
size_t)DIFFPAGESIZE-offset;
1094 if (client->
difmap[mapcnt]!=(u32)(-1)) {
1095 DEBUG(
"Page %llu is at %lu\n", (
unsigned long long)mapcnt,
1096 (
unsigned long)(client->
difmap[mapcnt]));
1098 if (read(client->
difffile, buf, rdlen) != rdlen)
return -1;
1100 DEBUG(
"Page %llu is not here, we read the original one\n",
1101 (
unsigned long long)mapcnt);
1104 len-=rdlen; a+=rdlen; buf+=rdlen;
1123 off_t mapcnt,mapl,maph;
1130 DEBUG(
"Asked to write %u bytes at %llu.\n", (
unsigned int)len, (
unsigned long long)a);
1134 for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
1136 offset=a-pagestart ;
1137 wrlen=(0<DIFFPAGESIZE-offset && len<(size_t)(DIFFPAGESIZE-offset)) ?
1138 len : (
size_t)DIFFPAGESIZE-offset;
1140 if (client->
difmap[mapcnt]!=(u32)(-1)) {
1141 DEBUG(
"Page %llu is at %lu\n", (
unsigned long long)mapcnt,
1142 (
unsigned long)(client->
difmap[mapcnt])) ;
1144 client->
difmap[mapcnt]*DIFFPAGESIZE+offset);
1145 if (write(client->
difffile, buf, wrlen) != wrlen)
return -1 ;
1149 DEBUG(
"Page %llu is not here, we put it at %lu\n",
1150 (
unsigned long long)mapcnt,
1151 (
unsigned long)(client->
difmap[mapcnt]));
1155 memcpy(pagebuf+offset,buf,wrlen) ;
1156 if (write(client->
difffile, pagebuf, DIFFPAGESIZE) !=
1160 len-=wrlen ; a+=wrlen ; buf+=wrlen ;
1186 for (i = 0; i < client->
export->len; i++) {
1207 if(i<client->export->len) {
1213 fallocate(prev.
fhandle, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, curoff, curlen);
1216 }
while(i < client->export->len && cur.
startoff < (req->
from + req->
len));
1217 DEBUG(
"Performed TRIM request from %llu to %llu", (
unsigned long long) req->
from, (
unsigned long long) req->
len);
1219 DEBUG(
"Ignoring TRIM request (not supported on current platform");
1224 static void send_reply(uint32_t opt,
int net, uint32_t reply_type,
size_t datasize,
void* data) {
1226 reply_type = htonl(reply_type);
1227 uint32_t datsize = htonl(datasize);
1229 struct iovec v_data[] = {
1231 { &opt,
sizeof(opt) },
1232 { &reply_type,
sizeof(reply_type) },
1233 { &datsize,
sizeof(datsize) },
1236 size_t total =
sizeof(
magic) +
sizeof(opt) +
sizeof(reply_type) +
sizeof(datsize) + datasize;
1237 ssize_t sent = writev(net, v_data, 5);
1239 perror(
"E: couldn't write enough data:");
1248 if (read(net, &namelen,
sizeof(namelen)) < 0) {
1249 err(
"Negotiation failed/7: %m");
1252 namelen = ntohl(namelen);
1253 name = malloc(namelen+1);
1255 if (read(net, name, namelen) < 0) {
1256 err(
"Negotiation failed/8: %m");
1260 for(i=0; i<servers->len; i++) {
1274 err(
"Negotiation failed/8a: Requested export not found");
1279 static void handle_list(uint32_t opt,
int net, GArray* servers, uint32_t cflags) {
1283 char *ptr = buf +
sizeof(
len);
1285 if (read(net, &len,
sizeof(len)) < 0)
1286 err(
"Negotiation failed/8: %m");
1296 for(i=0; i<servers->len; i++) {
1299 memcpy(buf, &len,
sizeof(len));
1315 uint16_t smallflags = 0;
1318 memset(zeros,
'\0',
sizeof(zeros));
1320 if(phase & NEG_MODERN) {
1323 if(phase & NEG_INIT) {
1330 if(phase & NEG_MODERN) {
1337 if (write(net, &magic,
sizeof(magic)) < 0) {
1343 if ((phase & NEG_MODERN) && (phase & NEG_INIT)) {
1349 err(
"programmer error");
1350 smallflags = htons(smallflags);
1351 if (write(net, &smallflags,
sizeof(uint16_t)) < 0)
1353 if (read(net, &cflags,
sizeof(cflags)) < 0)
1355 cflags = htonl(cflags);
1357 if (read(net, &magic,
sizeof(magic)) < 0)
1364 if (read(net, &opt,
sizeof(opt)) < 0)
1392 if (write(net, &size_host, 8) < 0)
1393 err(
"Negotiation failed/9: %m");
1406 flags = htonl(flags);
1407 if (write(client->
net, &flags, 4) < 0)
1408 err(
"Negotiation failed/10: %m");
1411 smallflags = (uint16_t)(flags & ~((uint16_t)0));
1412 smallflags = htons(smallflags);
1413 if (write(client->
net, &smallflags,
sizeof(smallflags)) < 0) {
1414 err(
"Negotiation failed/11: %m");
1418 if (write(client->
net, zeros, 124) < 0)
1419 err(
"Negotiation failed/12: %m");
1424 #define SEND(net,reply) { writeit( net, &reply, sizeof( reply )); \
1425 if (client->transactionlogfd != -1) \
1426 writeit(client->transactionlogfd, &reply, sizeof(reply)); }
1428 #define ERROR(client,reply,errcode) { reply.error = htonl(errcode); SEND(client->net,reply); reply.error = 0; }
1441 gboolean go_on=TRUE;
1446 DEBUG(
"Entering request loop!\n");
1460 readit(client->
net, &request,
sizeof(request));
1465 request.
type = ntohl(request.
type);
1467 len = ntohl(request.
len);
1470 (
unsigned long long)request.
from,
1471 (
unsigned long long)request.
from / 512, len);
1474 err(
"Not enough magic.");
1479 if (request.
from + len < request.
from) {
1480 DEBUG(
"[Number too large!]");
1481 ERROR(client, reply, EINVAL);
1487 ERROR(client, reply, EINVAL);
1495 msg(LOG_DEBUG,
"oversized request (this is not a problem)");
1504 msg(LOG_INFO,
"Disconnect request received.");
1515 DEBUG(
"wr: net->buf, ");
1518 DEBUG(
"buf->exp, ");
1521 DEBUG(
"[WRITE to READONLY!]");
1522 ERROR(client, reply, EPERM);
1528 DEBUG(
"Write failed: %m" );
1529 ERROR(client, reply, errno);
1534 request.
from += currlen;
1544 DEBUG(
"Flush failed: %m");
1545 ERROR(client, reply, errno);
1553 DEBUG(
"exp->buf, ");
1561 DEBUG(
"Read failed: %m");
1562 ERROR(client, reply, errno);
1566 DEBUG(
"buf->net, ");
1569 request.
from += currlen;
1580 if (
exptrim(&request, client)) {
1581 DEBUG(
"Trim failed: %m");
1582 ERROR(client, reply, errno);
1589 DEBUG (
"Ignoring unknown command\n");
1603 off_t laststartoff = 0, lastsize = 0;
1616 gchar* error_string;
1622 O_RDONLY : (O_RDWR | (cancreate?O_CREAT:0));
1625 tmpname=g_strdup_printf(
"%s.%d-XXXXXX", client->
exportname, i);
1626 DEBUG(
"Opening %s\n", tmpname );
1627 fi.
fhandle = mkstemp(tmpname);
1630 tmpname=g_strdup_printf(
"%s.%d", client->
exportname, i);
1634 DEBUG(
"Opening %s\n", tmpname );
1635 fi.
fhandle = open(tmpname, mode, 0x600);
1636 if(fi.
fhandle == -1 && mode == O_RDWR) {
1638 fi.
fhandle = open(tmpname, O_RDONLY);
1650 if(multifile && i>0)
1652 error_string=g_strdup_printf(
1653 "Could not open exported file %s: %%m",
1661 fi.
startoff = laststartoff + lastsize;
1662 g_array_append_val(client->
export, fi);
1671 if (!lastsize && cancreate) {
1674 err(
"Could not expand file: %m");
1680 if(!multifile || temporary)
1685 client->
exportsize = laststartoff + lastsize;
1691 err(
"Size of exported file is too big\n");
1697 msg(LOG_INFO,
"Size of exported file/device is %llu", (
unsigned long long)client->
exportsize);
1699 msg(LOG_INFO,
"Total number of files: %d", i);
1706 err(
"Failed to allocate string for diff file name");
1710 msg(LOG_INFO,
"About to create map and diff file %s", client->
difffilename) ;
1712 if (client->
difffile<0)
err(
"Could not create diff file (%m)") ;
1714 err(
"Could not allocate memory") ;
1731 if(command && *command) {
1732 cmd = g_strdup_printf(command, file);
1752 S_IRUSR | S_IWUSR)))
1753 g_warning(
"Could not open transaction log %s",
1791 struct sockaddr_storage netaddr;
1792 struct sockaddr_in *netaddr4 = NULL;
1793 struct sockaddr_in6 *netaddr6 = NULL;
1794 socklen_t addrinlen =
sizeof(
struct sockaddr_storage );
1795 struct addrinfo hints;
1796 struct addrinfo *ai = NULL;
1797 char peername[NI_MAXHOST];
1798 char netname[NI_MAXHOST];
1804 if (getpeername(net, (
struct sockaddr *) &(client->
clientaddr), &addrinlen) < 0) {
1805 msg(LOG_INFO,
"getpeername failed: %m");
1809 if((e = getnameinfo((
struct sockaddr *)&(client->
clientaddr), addrinlen,
1810 peername, sizeof (peername), NULL, 0, NI_NUMERICHOST))) {
1811 msg(LOG_INFO,
"getnameinfo failed: %s", gai_strerror(e));
1815 memset(&hints,
'\0',
sizeof (hints));
1816 hints.ai_flags = AI_ADDRCONFIG;
1817 e = getaddrinfo(peername, NULL, &hints, &ai);
1820 msg(LOG_INFO,
"getaddrinfo failed: %s", gai_strerror(e));
1827 msg(LOG_DEBUG,
"virtualization is off");
1831 msg(LOG_DEBUG,
"virtstyle iphash");
1832 for(i=0;i<strlen(peername);i++) {
1833 if(peername[i]==
'.') {
1838 msg(LOG_DEBUG,
"virststyle ipliteral");
1843 memcpy(&netaddr, &(client->
clientaddr), addrinlen);
1845 assert((ai->ai_family == AF_INET) || (ai->ai_family == AF_INET6));
1846 if(ai->ai_family == AF_INET) {
1848 }
else if(ai->ai_family == AF_INET6) {
1851 uint8_t* addrptr = ((
struct sockaddr*)&netaddr)->sa_data;
1852 for(
int i = 0; i < addrbits; i+=8) {
1854 masklen = masklen > 0 ? masklen : 0;
1859 getnameinfo((
struct sockaddr *) &netaddr, addrinlen,
1860 netname,
sizeof (netname), NULL, 0, NI_NUMERICHOST);
1861 tmp=g_strdup_printf(
"%s/%s", netname, peername);
1870 msg(LOG_INFO,
"connect from %s, assigned file is %s",
1891 sigemptyset(&newset);
1892 sigaddset(&newset, SIGCHLD);
1893 sigaddset(&newset, SIGTERM);
1894 sigprocmask(SIG_BLOCK, &newset, &oldset);
1897 msg(LOG_ERR,
"Could not fork (%s)", strerror(errno));
1903 pidp = g_malloc(
sizeof(pid_t));
1905 g_hash_table_insert(
children, pidp, pidp);
1909 signal(SIGCHLD, SIG_DFL);
1910 signal(SIGTERM, SIG_DFL);
1911 signal(SIGHUP, SIG_DFL);
1913 sigprocmask(SIG_SETMASK, &oldset, NULL);
1920 struct sockaddr_storage addrin;
1921 socklen_t addrinlen =
sizeof(addrin);
1924 net = accept(sock, (
struct sockaddr *) &addrin, &addrinlen);
1926 err_nonfatal(
"Failed to accept socket connection: %m");
1949 msg(LOG_INFO,
"Spawned a child process");
1951 msg(LOG_ERR,
"Failed to spawn a child process");
1960 msg(LOG_ERR,
"Modern initial negotiation failed");
1966 msg(LOG_ERR,
"Max connections (%d) reached",
1971 sock_flags_old = fcntl(net, F_GETFL, 0);
1972 if (sock_flags_old == -1) {
1973 msg(LOG_ERR,
"Failed to get socket flags");
1977 sock_flags_new = sock_flags_old & ~O_NONBLOCK;
1978 if (sock_flags_new != sock_flags_old &&
1979 fcntl(net, F_SETFL, sock_flags_new) == -1) {
1980 msg(LOG_ERR,
"Failed to set socket to blocking mode");
1985 msg(LOG_ERR,
"Failed to set peername");
1990 msg(LOG_INFO,
"Client '%s' is not authorized to access",
2012 for (i = 0; i < servers->len; i++) {
2013 const SERVER *
const server = &g_array_index(servers,
SERVER, i);
2023 g_array_free(servers, FALSE);
2026 msg(LOG_INFO,
"Starting to serve");
2053 msg(LOG_INFO,
"Max connections reached");
2054 goto handle_connection_out;
2056 if((sock_flags_old = fcntl(net, F_GETFL, 0)) == -1) {
2057 err(
"fcntl F_GETFL");
2059 sock_flags_new = sock_flags_old & ~O_NONBLOCK;
2060 if (sock_flags_new != sock_flags_old &&
2061 fcntl(net, F_SETFL, sock_flags_new) == -1) {
2062 err(
"fcntl F_SETFL ~O_NONBLOCK");
2065 client = g_new0(
CLIENT, 1);
2072 goto handle_connection_out;
2075 msg(LOG_INFO,
"Unauthorized client");
2076 goto handle_connection_out;
2078 msg(LOG_INFO,
"Authorized client");
2086 sigemptyset(&newset);
2087 sigaddset(&newset, SIGCHLD);
2088 sigaddset(&newset, SIGTERM);
2089 sigprocmask(SIG_BLOCK, &newset, &oldset);
2090 if ((pid = fork()) < 0) {
2091 msg(LOG_INFO,
"Could not fork (%s)", strerror(errno));
2092 sigprocmask(SIG_SETMASK, &oldset, NULL);
2093 goto handle_connection_out;
2098 pidp = g_malloc(
sizeof(pid_t));
2100 g_hash_table_insert(
children, pidp, pidp);
2101 sigprocmask(SIG_SETMASK, &oldset, NULL);
2102 goto handle_connection_out;
2105 signal(SIGCHLD, SIG_DFL);
2106 signal(SIGTERM, SIG_DFL);
2107 signal(SIGHUP, SIG_DFL);
2108 sigprocmask(SIG_SETMASK, &oldset, NULL);
2112 for(i=0;i<servers->len;i++) {
2113 close(g_array_index(servers,
SERVER, i).socket);
2121 g_array_free(servers, FALSE);
2128 msg(LOG_INFO,
"Starting to serve");
2132 handle_connection_out:
2147 const GArray *
const servers) {
2150 for (i = 0; i < servers->len; ++i) {
2151 const SERVER server = g_array_index(servers,
SERVER, i);
2153 if (strcmp(servename, server.
servename) == 0)
2174 GArray *new_servers;
2175 const int old_len = servers->len;
2183 for (i = 0; i < new_servers->len; ++i) {
2184 SERVER new_server = g_array_index(new_servers,
SERVER, i);
2196 retval = servers->len - old_len;
2198 g_array_free(new_servers, TRUE);
2221 for(i=0;i<servers->len;i++) {
2223 if((sock=(g_array_index(servers,
SERVER, i)).socket) >= 0) {
2224 FD_SET(sock, &mset);
2225 max=sock>max?sock:max;
2230 FD_SET(sock, &mset);
2231 max=sock>max?sock:max;
2242 GError *gerror = NULL;
2244 msg(LOG_INFO,
"reconfiguration request received");
2250 msg(LOG_ERR,
"failed to append new servers: %s",
2253 for (i = servers->len - n; i < servers->
len; ++i) {
2254 const SERVER server = g_array_index(servers,
2257 if (server.
socket >= 0) {
2258 FD_SET(server.
socket, &mset);
2262 msg(LOG_INFO,
"reconfigured new server: %s",
2267 memcpy(&rset, &mset,
sizeof(fd_set));
2268 if(select(max+1, &rset, NULL, NULL, NULL)>0) {
2273 if(!FD_ISSET(sock, &rset)) {
2279 for(i=0; i < servers->len; i++) {
2282 serve=&(g_array_index(servers,
SERVER, i));
2286 if(FD_ISSET(serve->
socket, &rset)) {
2315 if (setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,
sizeof(
int)) == -1) {
2317 "failed to set socket option SO_REUSEADDR: %s",
2323 if (setsockopt(socket,SOL_SOCKET,SO_LINGER,&l,
sizeof(l)) == -1) {
2325 "failed to set socket option SO_LINGER: %s",
2329 if (setsockopt(socket,SOL_SOCKET,SO_KEEPALIVE,&yes,
sizeof(
int)) == -1) {
2331 "failed to set socket option SO_KEEPALIVE: %s",
2345 struct addrinfo hints;
2346 struct addrinfo *ai = NULL;
2370 memset(&hints,
'\0',
sizeof(hints));
2372 hints.ai_socktype = SOCK_STREAM;
2375 port = g_strdup_printf(
"%d", serve->
port);
2378 "failed to open an export socket: "
2379 "failed to convert a port number to a string: %s",
2384 e = getaddrinfo(serve->
listenaddr,port,&hints,&ai);
2390 "failed to open an export socket: "
2391 "failed to get address info: %s",
2401 if (ai->ai_family == AF_INET)
2402 ai->ai_family = AF_INET_SDP;
2403 else (ai->ai_family == AF_INET6)
2404 ai->ai_family = AF_INET6_SDP;
2407 if ((serve->
socket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) {
2409 "failed to open an export socket: "
2410 "failed to create a socket: %s",
2416 g_prefix_error(gerror,
"failed to open an export socket: ");
2420 DEBUG(
"Waiting for connections... bind, ");
2421 e = bind(serve->
socket, ai->ai_addr, ai->ai_addrlen);
2422 if (e != 0 && errno != EADDRINUSE) {
2424 "failed to open an export socket: "
2425 "failed to bind an address to a socket: %s",
2430 if (listen(serve->
socket, 1) < 0) {
2432 "failed to open an export socket: "
2433 "failed to start listening on a socket: %s",
2441 if (retval == -1 && serve->
socket >= 0) {
2451 GError **
const gerror) {
2452 struct addrinfo hints;
2453 struct addrinfo* ai = NULL;
2454 struct addrinfo* ai_bak;
2461 memset(&hints,
'\0',
sizeof(hints));
2462 hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
2463 hints.ai_socktype = SOCK_STREAM;
2464 hints.ai_family = AF_UNSPEC;
2465 hints.ai_protocol = IPPROTO_TCP;
2470 "failed to open a modern socket: "
2471 "failed to get address info: %s",
2479 if((sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol))<0) {
2481 "failed to open a modern socket: "
2482 "failed to create a socket: %s",
2488 g_prefix_error(gerror,
"failed to open a modern socket: ");
2492 if(bind(sock, ai->ai_addr, ai->ai_addrlen)) {
2509 "failed to open a modern socket: "
2510 "failed to bind an address to a socket: %s",
2515 if(listen(sock, 10) <0) {
2517 "failed to open a modern socket: "
2518 "failed to start listening on a socket: %s",
2530 if (retval == -1 && sock >= 0) {
2534 freeaddrinfo(ai_bak);
2543 const gchar *
const modernport) {
2545 struct sigaction sa;
2548 for(i=0;i<servers->len;i++) {
2549 GError *gerror = NULL;
2555 msg(LOG_ERR,
"failed to setup servers: %s",
2557 g_clear_error(&gerror);
2563 GError *gerror = NULL;
2564 if (
open_modern(modernaddr, modernport, &gerror) == -1) {
2565 msg(LOG_ERR,
"failed to setup servers: %s",
2567 g_clear_error(&gerror);
2574 sigemptyset(&sa.sa_mask);
2575 sigaddset(&sa.sa_mask, SIGTERM);
2576 sa.sa_flags = SA_RESTART;
2577 if(sigaction(SIGCHLD, &sa, NULL) == -1)
2578 err(
"sigaction: %m");
2581 sigemptyset(&sa.sa_mask);
2582 sigaddset(&sa.sa_mask, SIGCHLD);
2583 sa.sa_flags = SA_RESTART;
2584 if(sigaction(SIGTERM, &sa, NULL) == -1)
2585 err(
"sigaction: %m");
2588 sigemptyset(&sa.sa_mask);
2589 sa.sa_flags = SA_RESTART;
2590 if(sigaction(SIGHUP, &sa, NULL) == -1)
2591 err(
"sigaction: %m");
2601 #if !defined(NODAEMON)
2605 if(serve && !(serve->
port)) {
2613 strncpy(
pidftemplate,
"/var/run/nbd-server.%d.pid", 255);
2621 fprintf(pidf,
"%d\n", (
int)getpid());
2625 fprintf(stderr,
"Not fatal; continuing");
2629 #define daemonize(serve)
2640 void dousers(
const gchar *
const username,
const gchar *
const groupname) {
2645 gr = getgrnam(groupname);
2647 str = g_strdup_printf(
"Invalid group name: %s", groupname);
2650 if(setgid(gr->gr_gid)<0) {
2651 err(
"Could not set GID: %m");
2655 pw = getpwnam(username);
2657 str = g_strdup_printf(
"Invalid user name: %s", username);
2660 if(setuid(pw->pw_uid)<0) {
2661 err(
"Could not set UID: %m");
2668 GLogLevelFlags log_level,
2669 const gchar *message,
2672 int level=LOG_DEBUG;
2676 case G_LOG_FLAG_FATAL:
2677 case G_LOG_LEVEL_CRITICAL:
2678 case G_LOG_LEVEL_ERROR:
2681 case G_LOG_LEVEL_WARNING:
2684 case G_LOG_LEVEL_MESSAGE:
2685 case G_LOG_LEVEL_INFO:
2688 case G_LOG_LEVEL_DEBUG:
2694 syslog(level,
"%s", message);
2710 fprintf(stderr,
"Bad size of structure. Alignment problems?\n");
2711 exit(EXIT_FAILURE) ;
2716 modernsocks = g_array_new(FALSE, FALSE,
sizeof(
int));
2733 if (!(serve->
port)) {
2742 open(
"/dev/null", O_WRONLY);
2743 open(
"/dev/null", O_WRONLY);
2746 client=g_malloc(
sizeof(
CLIENT));
2757 if(!servers || !servers->len) {
2758 if(err && !(err->domain ==
NBDS_ERR
2760 g_warning(
"Could not parse config file: %s",
2761 err ? err->message :
"Unknown error");
2765 g_warning(
"Specifying an export on the command line is deprecated.");
2766 g_warning(
"Please use a configuration file instead.");
2769 if((!serve) && (!servers||!servers->len)) {
2771 g_message(
"No configured exports; quitting.");
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.
int get_filepos(GArray *export, off_t a, int *fhandle, off_t *foffset, size_t *maxbytes)
Get the file handle and offset, given an export offset.
int setup_serve(SERVER *const serve, GError **const gerror)
Connect a server's socket.
static void consume(int f, void *buf, size_t len, size_t bufsiz)
Consume data from an FD that we don't want.
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)
void sigterm_handler(int s)
Handle SIGTERM and dispatch it to our children.
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...
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)
Connect our servers.
uint32_t difffilelen
number of pages in difffile
#define NBD_FLAG_SEND_FUA
#define SEND(net, reply)
sending macro.
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.
#define F_COPYONWRITE
flag to tell us a file is exported using copyonwrite
SERVER * server
The server this client is getting data from.
static void handle_oldstyle_connection(GArray *const servers, SERVER *const serve)
#define F_FLUSH
Whether server wants FLUSH to be sent by the client.
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)
static int socket_accept(const int sock)
This parameter is a boolean.
#define F_SDP
flag to tell us the export should be done using the Socket Direct Protocol for RDMA ...
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
void setmysockopt(int sock)
off_t startoff
starting offset of this file
The configuration file is not found.
#define F_TEMPORARY
Whether the backing file is temporary and should be created then unlinked.
char pidftemplate[256]
template to be used for the filename of the PID file
#define NBD_REP_ERR_POLICY
static pid_t spawn_child()
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.
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.
GArray * modernsocks
Sockets for the modern handler.
int exptrim(struct nbd_request *req, CLIENT *client)
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
void err_nonfatal(const char *s)
#define F_AUTOREADONLY
flag to tell us a file is set to autoreadonly
gpointer target
Pointer to where the data of this parameter should be written.
int do_run(gchar *command, gchar *file)
Run a command.
void usage(char *errmsg,...)
#define NBD_OPT_EXPORT_NAME
Underlying system call or library error.
int open_modern(const gchar *const addr, const gchar *const port, GError **const gerror)
unsigned int port
port we're exporting this file at
gboolean required
Whether this is a required (as opposed to optional) parameter.
#define F_TRIM
Whether server wants TRIM (discard) to be sent by the client.
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.
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.
int socket
The socket of this server.
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.
SERVER * cmdline(int argc, char *argv[])
Parse the command line.
#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
static void readit(int f, void *buf, size_t len)
Read data from a file descriptor into a buffer.
char pidfname[256]
name of our PID file
Failed to get address info.
Variables associated with an open file.
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)
void daemonize(SERVER *serve)
Go daemon (unless we specified at compile time that we didn't want this)
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.
#define F_SPARSE
flag to tell us copyronwrite should use a sparse file
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 sigchld_handler(int s)
Signal handler for SIGCHLD.
GArray * do_cfile_dir(gchar *dir, GError **e)
Parse config file snippets in a directory.
Literal IP address as part of the filename.
int transactionlogfd
fd for transaction log
void myseek(int handle, off_t a)
seek to a position in a file, with error handling.
void err(const char *s) G_GNUC_NORETURN
int max_connections
maximum number of opened connections
VIRT_STYLE virtstyle
The style of virtualization, if any.
#define NBD_FLAG_READ_ONLY
static volatile sig_atomic_t is_sighup_caught
Flag set by SIGHUP handler to mark a reconfiguration request.
Failed to set SO_REUSEADDR to a socket.
int append_serve(const SERVER *const s, GArray *const a)
append new server to array
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.
int socket_family
family of the socket
uint64_t expected_size
size of the exported file as it was told to us through configuration
gchar * prerun
command to be ran after connecting a client, but before starting to serve
#define NBD_FLAG_SEND_TRIM
int main(int argc, char **argv)
This parameter is an integer.
#define NBD_CMD_MASK_COMMAND
gchar * group
group we run running as
uint64_t exportsize
size of the file we're exporting
#define F_SYNC
Whether to fsync() after a write.
char * authname
filename of the authorization file
static void writeit(int f, void *buf, size_t len)
Write data from a buffer into a filedescriptor.
gboolean modern
client was negotiated using modern negotiation protocol
#define F_FUA
Whether server wants FUA to be sent by the client.
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.
#define NBD_FLAG_FIXED_NEWSTYLE
#define F_READONLY
Per-export flags:
A value is not supported in this build.
#define NBD_FLAG_ROTATIONAL
A directory requested does not exist.
char * exportname
(processed) filename of the file we're exporting
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.
#define ERROR(client, reply, errcode)
error macro.
void negotiate(int sock, u64 *rsize64, u32 *flags, char *name, uint32_t needed_flags, uint32_t client_flags, uint32_t do_opts)
Configuration file values of the "generic" section.
Configuration file values.
#define F_ROTATIONAL
Whether server wants the client to implement the elevator algorithm.
A value is syntactically invalid.
GArray * parse_cfile(gchar *f, struct generic_conf *genconf, GError **e)
Parse the config file.
int mainloop(CLIENT *client)
Serve a file to a single client.
#define F_MULTIFILE
flag to tell us a file is exported using -m
Failed to create a socket.
int authorized_client(CLIENT *opts)
Check whether a client is allowed to connect.
#define NBD_REP_ERR_UNSUP