Bug Summary

File:nbd-server.c
Location:line 1573, column 26
Description:Access to field 'exportsize' results in a dereference of a null pointer (loaded from variable 'client')

Annotated Source Code

1/*
2 * Network Block Device - server
3 *
4 * Copyright 1996-1998 Pavel Machek, distribute under GPL
5 * <pavel@atrey.karlin.mff.cuni.cz>
6 * Copyright 2001-2004 Wouter Verhelst <wouter@debian.org>
7 * Copyright 2002 Anton Altaparmakov <aia21@cam.ac.uk>
8 *
9 * Version 1.0 - hopefully 64-bit-clean
10 * Version 1.1 - merging enhancements from Josh Parsons, <josh@coombs.anu.edu.au>
11 * Version 1.2 - autodetect size of block devices, thanx to Peter T. Breuer" <ptb@it.uc3m.es>
12 * Version 1.5 - can compile on Unix systems that don't have 64 bit integer
13 * type, or don't have 64 bit file offsets by defining FS_32BIT
14 * in compile options for nbd-server *only*. This can be done
15 * with make FSCHOICE=-DFS_32BIT nbd-server. (I don't have the
16 * original autoconf input file, or I would make it a configure
17 * option.) Ken Yap <ken@nlc.net.au>.
18 * Version 1.6 - fix autodetection of block device size and really make 64 bit
19 * clean on 32 bit machines. Anton Altaparmakov <aia21@cam.ac.uk>
20 * Version 2.0 - Version synchronised with client
21 * Version 2.1 - Reap zombie client processes when they exit. Removed
22 * (uncommented) the _IO magic, it's no longer necessary. Wouter
23 * Verhelst <wouter@debian.org>
24 * Version 2.2 - Auto switch to read-only mode (usefull for floppies).
25 * Version 2.3 - Fixed code so that Large File Support works. This
26 * removes the FS_32BIT compile-time directive; define
27 * _FILE_OFFSET_BITS=64 and _LARGEFILE_SOURCE if you used to be
28 * using FS_32BIT. This will allow you to use files >2GB instead of
29 * having to use the -m option. Wouter Verhelst <wouter@debian.org>
30 * Version 2.4 - Added code to keep track of children, so that we can
31 * properly kill them from initscripts. Add a call to daemon(),
32 * so that processes don't think they have to wait for us, which is
33 * interesting for initscripts as well. Wouter Verhelst
34 * <wouter@debian.org>
35 * Version 2.5 - Bugfix release: forgot to reset child_arraysize to
36 * zero after fork()ing, resulting in nbd-server going berserk
37 * when it receives a signal with at least one child open. Wouter
38 * Verhelst <wouter@debian.org>
39 * 10/10/2003 - Added socket option SO_KEEPALIVE (sf.net bug 819235);
40 * rectified type of mainloop::size_host (sf.net bugs 814435 and
41 * 817385); close the PID file after writing to it, so that the
42 * daemon can actually be found. Wouter Verhelst
43 * <wouter@debian.org>
44 * 10/10/2003 - Size of the data "size_host" was wrong and so was not
45 * correctly put in network endianness. Many types were corrected
46 * (size_t and off_t instead of int). <vspaceg@sourceforge.net>
47 * Version 2.6 - Some code cleanup.
48 * Version 2.7 - Better build system.
49 * 11/02/2004 - Doxygenified the source, modularized it a bit. Needs a
50 * lot more work, but this is a start. Wouter Verhelst
51 * <wouter@debian.org>
52 * 16/03/2010 - Add IPv6 support.
53 * Kitt Tientanopajai <kitt@kitty.in.th>
54 * Neutron Soutmun <neo.neutron@gmail.com>
55 * Suriya Soutmun <darksolar@gmail.com>
56 */
57
58/* Includes LFS defines, which defines behaviours of some of the following
59 * headers, so must come before those */
60#include "lfs.h"
61
62#include <sys/types.h>
63#include <sys/socket.h>
64#include <sys/stat.h>
65#include <sys/select.h>
66#include <sys/wait.h>
67#ifdef HAVE_SYS_IOCTL_H1
68#include <sys/ioctl.h>
69#endif
70#include <sys/param.h>
71#ifdef HAVE_SYS_MOUNT_H1
72#include <sys/mount.h>
73#endif
74#include <signal.h>
75#include <errno(*__errno_location ()).h>
76#include <netinet/tcp.h>
77#include <netinet/in.h>
78#include <netdb.h>
79#include <syslog.h>
80#include <unistd.h>
81#include <stdbool.h>
82#include <stdio.h>
83#include <stdlib.h>
84#include <string.h>
85#include <fcntl.h>
86#if HAVE_FALLOC_PH1
87#include <linux1/falloc.h>
88#endif
89#include <arpa/inet.h>
90#include <strings.h>
91#include <dirent.h>
92#include <unistd.h>
93#include <getopt.h>
94#include <pwd.h>
95#include <grp.h>
96#include <dirent.h>
97
98#include <glib.h>
99
100/* used in cliserv.h, so must come first */
101#define MY_NAME"nbd_server" "nbd_server"
102#include "cliserv.h"
103
104#ifdef WITH_SDP
105#include <sdp_inet.h>
106#endif
107
108/** Default position of the config file */
109#ifndef SYSCONFDIR"/usr/local/etc"
110#define SYSCONFDIR"/usr/local/etc" "/etc"
111#endif
112#define CFILE"/usr/local/etc" "/nbd-server/config" SYSCONFDIR"/usr/local/etc" "/nbd-server/config"
113
114/** Where our config file actually is */
115gchar* config_file_pos;
116
117/** What user we're running as */
118gchar* runuser=NULL((void*)0);
119/** What group we're running as */
120gchar* rungroup=NULL((void*)0);
121/** whether to export using the old negotiation protocol (port-based) */
122gboolean do_oldstyle=FALSE(0);
123
124/* Whether we should avoid forking */
125int dontfork = 0;
126
127/** Logging macros, now nothing goes to syslog unless you say ISSERVER */
128#ifdef ISSERVER
129#define msg2(a,b)g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)b) syslog(a,b)
130#define msg3(a,b,c)g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)b,c) syslog(a,b,c)
131#define msg4(a,b,c,d)g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)b,c,d) syslog(a,b,c,d)
132#else
133#define msg2(a,b)g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)b) g_message((char*)b)g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)b)
134#define msg3(a,b,c)g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)b,c) g_message((char*)b,c)g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)b,c)
135#define msg4(a,b,c,d)g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)b,c,d) g_message((char*)b,c,d)g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)b,c,d)
136#endif
137
138/* Debugging macros */
139//#define DODBG
140#ifdef DODBG
141#define DEBUG(...) printf(__VA_ARGS__)
142#else
143#define DEBUG(...)
144#endif
145#ifndef PACKAGE_VERSION"2.9.25"
146#define PACKAGE_VERSION"2.9.25" ""
147#endif
148/**
149 * The highest value a variable of type off_t can reach. This is a signed
150 * integer, so set all bits except for the leftmost one.
151 **/
152#define OFFT_MAX~((off_t)1<<(sizeof(off_t)*8 -1)) ~((off_t)1<<(sizeof(off_t)*8-1))
153#define LINELEN256 256 /**< Size of static buffer used to read the
154 authorization file (yuck) */
155#define BUFSIZE((1024*1024)+sizeof(struct nbd_reply)) ((1024*1024)+sizeof(struct nbd_reply)) /**< Size of buffer that can hold requests */
156#define DIFFPAGESIZE4096 4096 /**< diff file uses those chunks */
157#define F_READONLY1 1 /**< flag to tell us a file is readonly */
158#define F_MULTIFILE2 2 /**< flag to tell us a file is exported using -m */
159#define F_COPYONWRITE4 4 /**< flag to tell us a file is exported using
160 copyonwrite */
161#define F_AUTOREADONLY8 8 /**< flag to tell us a file is set to autoreadonly */
162#define F_SPARSE16 16 /**< flag to tell us copyronwrite should use a sparse file */
163#define F_SDP32 32 /**< flag to tell us the export should be done using the Socket Direct Protocol for RDMA */
164#define F_SYNC64 64 /**< Whether to fsync() after a write */
165#define F_FLUSH128 128 /**< Whether server wants FLUSH to be sent by the client */
166#define F_FUA256 256 /**< Whether server wants FUA to be sent by the client */
167#define F_ROTATIONAL512 512 /**< Whether server wants the client to implement the elevator algorithm */
168#define F_TEMPORARY1024 1024 /**< Whether the backing file is temporary and should be created then unlinked */
169#define F_TRIM2048 2048 /**< Whether server wants TRIM (discard) to be sent by the client */
170GHashTable *children;
171char pidfname[256]; /**< name of our PID file */
172char pidftemplate[256]; /**< template to be used for the filename of the PID file */
173char default_authname[] = SYSCONFDIR"/usr/local/etc" "/nbd-server/allow"; /**< default name of allow file */
174
175#define NEG_INIT(1 << 0) (1 << 0)
176#define NEG_OLD(1 << 1) (1 << 1)
177#define NEG_MODERN(1 << 2) (1 << 2)
178
179int modernsock=0; /**< Socket for the modern handler. Not used
180 if a client was only specified on the
181 command line; only port used if
182 oldstyle is set to false (and then the
183 command-line client isn't used, gna gna) */
184char* modern_listen; /**< listenaddr value for modernsock */
185char* modernport=NBD_DEFAULT_PORT"10809"; /**< Port number on which to listen for
186 new-style nbd-client connections */
187
188bool_Bool logged_oversized=false0; /**< whether we logged oversized requests already */
189
190/**
191 * Types of virtuatlization
192 **/
193typedef enum {
194 VIRT_NONE=0, /**< No virtualization */
195 VIRT_IPLIT, /**< Literal IP address as part of the filename */
196 VIRT_IPHASH, /**< Replacing all dots in an ip address by a / before
197 doing the same as in IPLIT */
198 VIRT_CIDR, /**< Every subnet in its own directory */
199} VIRT_STYLE;
200
201/**
202 * Variables associated with a server.
203 **/
204typedef struct {
205 gchar* exportname; /**< (unprocessed) filename of the file we're exporting */
206 off_t expected_size; /**< size of the exported file as it was told to
207 us through configuration */
208 gchar* listenaddr; /**< The IP address we're listening on */
209 unsigned int port; /**< port we're exporting this file at */
210 char* authname; /**< filename of the authorization file */
211 int flags; /**< flags associated with this exported file */
212 int socket; /**< The socket of this server. */
213 int socket_family; /**< family of the socket */
214 VIRT_STYLE virtstyle;/**< The style of virtualization, if any */
215 uint8_t cidrlen; /**< The length of the mask when we use
216 CIDR-style virtualization */
217 gchar* prerun; /**< command to be ran after connecting a client,
218 but before starting to serve */
219 gchar* postrun; /**< command that will be ran after the client
220 disconnects */
221 gchar* servename; /**< name of the export as selected by nbd-client */
222 int max_connections; /**< maximum number of opened connections */
223 gchar* transactionlog;/**< filename for transaction log */
224} SERVER;
225
226/**
227 * Variables associated with a client socket.
228 **/
229typedef struct {
230 int fhandle; /**< file descriptor */
231 off_t startoff; /**< starting offset of this file */
232} FILE_INFO;
233
234typedef struct {
235 off_t exportsize; /**< size of the file we're exporting */
236 char *clientname; /**< peer */
237 char *exportname; /**< (processed) filename of the file we're exporting */
238 GArray *export; /**< array of FILE_INFO of exported files;
239 array size is always 1 unless we're
240 doing the multiple file option */
241 int net; /**< The actual client socket */
242 SERVER *server; /**< The server this client is getting data from */
243 char* difffilename; /**< filename of the copy-on-write file, if any */
244 int difffile; /**< filedescriptor of copyonwrite file. @todo
245 shouldn't this be an array too? (cfr export) Or
246 make -m and -c mutually exclusive */
247 u32 difffilelen; /**< number of pages in difffile */
248 u32 *difmap; /**< see comment on the global difmap for this one */
249 gboolean modern; /**< client was negotiated using modern negotiation protocol */
250 int transactionlogfd;/**< fd for transaction log */
251} CLIENT;
252
253/**
254 * Type of configuration file values
255 **/
256typedef enum {
257 PARAM_INT, /**< This parameter is an integer */
258 PARAM_INT64, /**< This parameter is an integer */
259 PARAM_STRING, /**< This parameter is a string */
260 PARAM_BOOL, /**< This parameter is a boolean */
261} PARAM_TYPE;
262
263/**
264 * Configuration file values
265 **/
266typedef struct {
267 gchar *paramname; /**< Name of the parameter, as it appears in
268 the config file */
269 gboolean required; /**< Whether this is a required (as opposed to
270 optional) parameter */
271 PARAM_TYPE ptype; /**< Type of the parameter. */
272 gpointer target; /**< Pointer to where the data of this
273 parameter should be written. If ptype is
274 PARAM_BOOL, the data is or'ed rather than
275 overwritten. */
276 gint flagval; /**< Flag mask for this parameter in case ptype
277 is PARAM_BOOL. */
278} PARAM;
279
280/**
281 * Translate a command name into human readable form
282 *
283 * @param command The command number (after applying NBD_CMD_MASK_COMMAND)
284 * @return pointer to the command name
285 **/
286static inline const char * getcommandname(uint64_t command) {
287 switch (command) {
288 case NBD_CMD_READ:
289 return "NBD_CMD_READ";
290 case NBD_CMD_WRITE:
291 return "NBD_CMD_WRITE";
292 case NBD_CMD_DISC:
293 return "NBD_CMD_DISC";
294 case NBD_CMD_FLUSH:
295 return "NBD_CMD_FLUSH";
296 default:
297 break;
298 }
299 return "UNKNOWN";
300}
301
302/**
303 * Check whether a client is allowed to connect. Works with an authorization
304 * file which contains one line per machine, no wildcards.
305 *
306 * @param opts The client who's trying to connect.
307 * @return 0 - authorization refused, 1 - OK
308 **/
309int authorized_client(CLIENT *opts) {
310 const char *ERRMSG="Invalid entry '%s' in authfile '%s', so, refusing all connections.";
311 FILE *f ;
312 char line[LINELEN256];
313 char *tmp;
314 struct in_addr addr;
315 struct in_addr client;
316 struct in_addr cltemp;
317 int len;
318
319 if ((f=fopen(opts->server->authname,"r"))==NULL((void*)0)) {
320 msg4(LOG_INFO,"Can't open authorization file %s (%s).",g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"Can't open authorization file %s (%s)."
,opts->server->authname,strerror((*__errno_location ())
))
321 opts->server->authname,strerror(errno))g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"Can't open authorization file %s (%s)."
,opts->server->authname,strerror((*__errno_location ())
))
;
322 return 1 ;
323 }
324
325 inet_aton(opts->clientname, &client);
326 while (fgets(line,LINELEN256,f)!=NULL((void*)0)) {
327 if((tmp=strchr(line, '/'))) {
328 if(strlen(line)<=tmp-line) {
329 msg4(LOG_CRIT, ERRMSG, line, opts->server->authname)g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)ERRMSG,line,
opts->server->authname)
;
330 return 0;
331 }
332 *(tmp++)=0;
333 if(!inet_aton(line,&addr)) {
334 msg4(LOG_CRIT, ERRMSG, line, opts->server->authname)g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)ERRMSG,line,
opts->server->authname)
;
335 return 0;
336 }
337 len=strtol(tmp, NULL((void*)0), 0);
338 addr.s_addr>>=32-len;
339 addr.s_addr<<=32-len;
340 memcpy(&cltemp,&client,sizeof(client));
341 cltemp.s_addr>>=32-len;
342 cltemp.s_addr<<=32-len;
343 if(addr.s_addr == cltemp.s_addr) {
344 return 1;
345 }
346 }
347 if (strncmp(line,opts->clientname,strlen(opts->clientname))==0) {
348 fclose(f);
349 return 1;
350 }
351 }
352 fclose(f);
353 return 0;
354}
355
356/**
357 * Read data from a file descriptor into a buffer
358 *
359 * @param f a file descriptor
360 * @param buf a buffer
361 * @param len the number of bytes to be read
362 **/
363static inline void readit(int f, void *buf, size_t len) {
364 ssize_t res;
365 while (len > 0) {
366 DEBUG("*");
367 if ((res = read(f, buf, len)) <= 0) {
368 if(errno(*__errno_location ()) != EAGAIN11) {
369 err("Read failed: %m");
370 }
371 } else {
372 len -= res;
373 buf += res;
374 }
375 }
376}
377
378/**
379 * Consume data from an FD that we don't want
380 *
381 * @param f a file descriptor
382 * @param buf a buffer
383 * @param len the number of bytes to consume
384 * @param bufsiz the size of the buffer
385 **/
386static inline void consume(int f, void * buf, size_t len, size_t bufsiz) {
387 size_t curlen;
388 while (len>0) {
389 curlen = (len>bufsiz)?bufsiz:len;
390 readit(f, buf, curlen);
391 len -= curlen;
392 }
393}
394
395
396/**
397 * Write data from a buffer into a filedescriptor
398 *
399 * @param f a file descriptor
400 * @param buf a buffer containing data
401 * @param len the number of bytes to be written
402 **/
403static inline void writeit(int f, void *buf, size_t len) {
404 ssize_t res;
405 while (len > 0) {
406 DEBUG("+");
407 if ((res = write(f, buf, len)) <= 0)
408 err("Send failed: %m");
409 len -= res;
410 buf += res;
411 }
412}
413
414/**
415 * Print out a message about how to use nbd-server. Split out to a separate
416 * function so that we can call it from multiple places
417 */
418void usage() {
419 printf("This is nbd-server version " VERSION"2.9.25" "\n");
420 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"
421 "\t-r|--read-only\t\tread only\n"
422 "\t-m|--multi-file\t\tmultiple file\n"
423 "\t-c|--copy-on-write\tcopy on write\n"
424 "\t-C|--config-file\tspecify an alternate configuration file\n"
425 "\t-l|--authorize-file\tfile with list of hosts that are allowed to\n\t\t\t\tconnect.\n"
426 "\t-p|--pid-file\t\tspecify a filename to write our PID to\n"
427 "\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"
428 "\t-M|--max-connections\tspecify the maximum number of opened connections\n\n"
429 "\tif port is set to 0, stdin is used (for running from inetd).\n"
430 "\tif file_to_export contains '%%s', it is substituted with the IP\n"
431 "\t\taddress of the machine trying to connect\n"
432 "\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");
433 printf("Using configuration file %s\n", CFILE"/usr/local/etc" "/nbd-server/config");
434}
435
436/* Dumps a config file section of the given SERVER*, and exits. */
437void dump_section(SERVER* serve, gchar* section_header) {
438 printf("[%s]\n", section_header);
439 printf("\texportname = %s\n", serve->exportname);
440 printf("\tlistenaddr = %s\n", serve->listenaddr);
441 printf("\tport = %d\n", serve->port);
442 if(serve->flags & F_READONLY1) {
443 printf("\treadonly = true\n");
444 }
445 if(serve->flags & F_MULTIFILE2) {
446 printf("\tmultifile = true\n");
447 }
448 if(serve->flags & F_COPYONWRITE4) {
449 printf("\tcopyonwrite = true\n");
450 }
451 if(serve->expected_size) {
452 printf("\tfilesize = %lld\n", (long long int)serve->expected_size);
453 }
454 if(serve->authname) {
455 printf("\tauthfile = %s\n", serve->authname);
456 }
457 exit(EXIT_SUCCESS0);
458}
459
460/**
461 * Parse the command line.
462 *
463 * @param argc the argc argument to main()
464 * @param argv the argv argument to main()
465 **/
466SERVER* cmdline(int argc, char *argv[]) {
467 int i=0;
468 int nonspecial=0;
469 int c;
470 struct option long_options[] = {
471 {"read-only", no_argument0, NULL((void*)0), 'r'},
472 {"multi-file", no_argument0, NULL((void*)0), 'm'},
473 {"copy-on-write", no_argument0, NULL((void*)0), 'c'},
474 {"dont-fork", no_argument0, NULL((void*)0), 'd'},
475 {"authorize-file", required_argument1, NULL((void*)0), 'l'},
476 {"config-file", required_argument1, NULL((void*)0), 'C'},
477 {"pid-file", required_argument1, NULL((void*)0), 'p'},
478 {"output-config", required_argument1, NULL((void*)0), 'o'},
479 {"max-connection", required_argument1, NULL((void*)0), 'M'},
480 {0,0,0,0}
481 };
482 SERVER *serve;
483 off_t es;
484 size_t last;
485 char suffix;
486 gboolean do_output=FALSE(0);
487 gchar* section_header="";
488 gchar** addr_port;
489
490 if(argc==1) {
491 return NULL((void*)0);
492 }
493 serve=g_new0(SERVER, 1)((SERVER *) g_malloc0_n ((1), sizeof (SERVER)));
494 serve->authname = g_strdup(default_authname);
495 serve->virtstyle=VIRT_IPLIT;
496 while((c=getopt_long(argc, argv, "-C:cdl:mo:rp:M:", long_options, &i))>=0) {
497 switch (c) {
498 case 1:
499 /* non-option argument */
500 switch(nonspecial++) {
501 case 0:
502 if(strchr(optarg, ':') == strrchr(optarg, ':')) {
503 addr_port=g_strsplit(optarg, ":", 2);
504
505 /* Check for "@" - maybe user using this separator
506 for IPv4 address */
507 if(!addr_port[1]) {
508 g_strfreev(addr_port);
509 addr_port=g_strsplit(optarg, "@", 2);
510 }
511 } else {
512 addr_port=g_strsplit(optarg, "@", 2);
513 }
514
515 if(addr_port[1]) {
516 serve->port=strtol(addr_port[1], NULL((void*)0), 0);
517 serve->listenaddr=g_strdup(addr_port[0]);
518 } else {
519 serve->listenaddr=NULL((void*)0);
520 serve->port=strtol(addr_port[0], NULL((void*)0), 0);
521 }
522 g_strfreev(addr_port);
523 break;
524 case 1:
525 serve->exportname = g_strdup(optarg);
526 if(serve->exportname[0] != '/') {
527 fprintf(stderrstderr, "E: The to be exported file needs to be an absolute filename!\n");
528 exit(EXIT_FAILURE1);
529 }
530 break;
531 case 2:
532 last=strlen(optarg)-1;
533 suffix=optarg[last];
534 if (suffix == 'k' || suffix == 'K' ||
535 suffix == 'm' || suffix == 'M')
536 optarg[last] = '\0';
537 es = (off_t)atoll(optarg);
538 switch (suffix) {
539 case 'm':
540 case 'M': es <<= 10;
541 case 'k':
542 case 'K': es <<= 10;
543 default : break;
544 }
545 serve->expected_size = es;
546 break;
547 }
548 break;
549 case 'r':
550 serve->flags |= F_READONLY1;
551 break;
552 case 'm':
553 serve->flags |= F_MULTIFILE2;
554 break;
555 case 'o':
556 do_output = TRUE(!(0));
557 section_header = g_strdup(optarg);
558 break;
559 case 'p':
560 strncpy(pidftemplate, optarg, 256);
561 break;
562 case 'c':
563 serve->flags |=F_COPYONWRITE4;
564 break;
565 case 'd':
566 dontfork = 1;
567 break;
568 case 'C':
569 g_free(config_file_pos);
570 config_file_pos=g_strdup(optarg);
571 break;
572 case 'l':
573 g_free(serve->authname);
574 serve->authname=g_strdup(optarg);
575 break;
576 case 'M':
577 serve->max_connections = strtol(optarg, NULL((void*)0), 0);
578 break;
579 default:
580 usage();
581 exit(EXIT_FAILURE1);
582 break;
583 }
584 }
585 /* What's left: the port to export, the name of the to be exported
586 * file, and, optionally, the size of the file, in that order. */
587 if(nonspecial<2) {
588 g_free(serve);
589 serve=NULL((void*)0);
590 } else {
591 do_oldstyle = TRUE(!(0));
592 }
593 if(do_output) {
594 if(!serve) {
595 g_critical("Need a complete configuration on the command line to output a config file section!")g_log (((gchar*) 0), G_LOG_LEVEL_CRITICAL, "Need a complete configuration on the command line to output a config file section!"
)
;
596 exit(EXIT_FAILURE1);
597 }
598 dump_section(serve, section_header);
599 }
600 return serve;
601}
602
603/**
604 * Error codes for config file parsing
605 **/
606typedef enum {
607 CFILE_NOTFOUND, /**< The configuration file is not found */
608 CFILE_MISSING_GENERIC, /**< The (required) group "generic" is missing */
609 CFILE_KEY_MISSING, /**< A (required) key is missing */
610 CFILE_VALUE_INVALID, /**< A value is syntactically invalid */
611 CFILE_VALUE_UNSUPPORTED,/**< A value is not supported in this build */
612 CFILE_PROGERR, /**< Programmer error */
613 CFILE_NO_EXPORTS, /**< A config file was specified that does not
614 define any exports */
615 CFILE_INCORRECT_PORT, /**< The reserved port was specified for an
616 old-style export. */
617 CFILE_DIR_UNKNOWN, /**< A directory requested does not exist*/
618 CFILE_READDIR_ERR, /**< Error occurred during readdir() */
619} CFILE_ERRORS;
620
621/**
622 * Remove a SERVER from memory. Used from the hash table
623 **/
624void remove_server(gpointer s) {
625 SERVER *server;
626
627 server=(SERVER*)s;
628 g_free(server->exportname);
629 if(server->authname)
630 g_free(server->authname);
631 if(server->listenaddr)
632 g_free(server->listenaddr);
633 if(server->prerun)
634 g_free(server->prerun);
635 if(server->postrun)
636 g_free(server->postrun);
637 if(server->transactionlog)
638 g_free(server->transactionlog);
639 g_free(server);
640}
641
642/**
643 * duplicate server
644 * @param s the old server we want to duplicate
645 * @return new duplicated server
646 **/
647SERVER* dup_serve(SERVER *s) {
648 SERVER *serve = NULL((void*)0);
649
650 serve=g_new0(SERVER, 1)((SERVER *) g_malloc0_n ((1), sizeof (SERVER)));
651 if(serve == NULL((void*)0))
652 return NULL((void*)0);
653
654 if(s->exportname)
655 serve->exportname = g_strdup(s->exportname);
656
657 serve->expected_size = s->expected_size;
658
659 if(s->listenaddr)
660 serve->listenaddr = g_strdup(s->listenaddr);
661
662 serve->port = s->port;
663
664 if(s->authname)
665 serve->authname = strdup(s->authname);
666
667 serve->flags = s->flags;
668 serve->socket = s->socket;
669 serve->socket_family = s->socket_family;
670 serve->virtstyle = s->virtstyle;
671 serve->cidrlen = s->cidrlen;
672
673 if(s->prerun)
674 serve->prerun = g_strdup(s->prerun);
675
676 if(s->postrun)
677 serve->postrun = g_strdup(s->postrun);
678
679 if(s->transactionlog)
680 serve->transactionlog = g_strdup(s->transactionlog);
681
682 if(s->servename)
683 serve->servename = g_strdup(s->servename);
684
685 serve->max_connections = s->max_connections;
686
687 return serve;
688}
689
690/**
691 * append new server to array
692 * @param s server
693 * @param a server array
694 * @return 0 success, -1 error
695 */
696int append_serve(SERVER *s, GArray *a) {
697 SERVER *ns = NULL((void*)0);
698 struct addrinfo hints;
699 struct addrinfo *ai = NULL((void*)0);
700 struct addrinfo *rp = NULL((void*)0);
701 char host[NI_MAXHOST1025];
702 gchar *port = NULL((void*)0);
703 int e;
704 int ret;
705
706 if(!s) {
707 err("Invalid parsing server");
708 return -1;
709 }
710
711 port = g_strdup_printf("%d", s->port);
712
713 memset(&hints,'\0',sizeof(hints));
714 hints.ai_family = AF_UNSPEC0;
715 hints.ai_socktype = SOCK_STREAMSOCK_STREAM;
716 hints.ai_flags = AI_ADDRCONFIG0x0020 | AI_PASSIVE0x0001;
717 hints.ai_protocol = IPPROTO_TCPIPPROTO_TCP;
718
719 e = getaddrinfo(s->listenaddr, port, &hints, &ai);
720
721 if (port)
722 g_free(port);
723
724 if(e == 0) {
725 for (rp = ai; rp != NULL((void*)0); rp = rp->ai_next) {
726 e = getnameinfo(rp->ai_addr, rp->ai_addrlen, host, sizeof(host), NULL((void*)0), 0, NI_NUMERICHOST1);
727
728 if (e != 0) { // error
729 fprintf(stderrstderr, "getnameinfo: %s\n", gai_strerror(e));
730 continue;
731 }
732
733 // duplicate server and set listenaddr to resolved IP address
734 ns = dup_serve (s);
735 if (ns) {
736 ns->listenaddr = g_strdup(host);
737 ns->socket_family = rp->ai_family;
738 g_array_append_val(a, *ns)g_array_append_vals (a, &(*ns), 1);
739 free(ns);
740 ns = NULL((void*)0);
741 }
742 }
743
744 ret = 0;
745 } else {
746 fprintf(stderrstderr, "getaddrinfo failed on listen host/address: %s (%s)\n", s->listenaddr ? s->listenaddr : "any", gai_strerror(e));
747 ret = -1;
748 }
749
750 if (ai)
751 freeaddrinfo(ai);
752
753 return ret;
754}
755
756/* forward definition of parse_cfile */
757GArray* parse_cfile(gchar* f, bool_Bool have_global, GError** e);
758
759/**
760 * Parse config file snippets in a directory. Uses readdir() and friends
761 * to find files and open them, then passes them on to parse_cfile
762 * with have_global set false
763 **/
764GArray* do_cfile_dir(gchar* dir, GError** e) {
765 DIR* dirh = opendir(dir);
766 GQuark errdomain = g_quark_from_string("do_cfile_dir");
767 struct dirent* de;
768 gchar* fname;
769 GArray* retval = NULL((void*)0);
770 GArray* tmp;
771
772 if(!dir) {
773 g_set_error(e, errdomain, CFILE_DIR_UNKNOWN, "Invalid directory specified: %s", strerror(errno(*__errno_location ())));
774 return NULL((void*)0);
775 }
776 errno(*__errno_location ())=0;
777 while((de = readdir(dirh))) {
778 int saved_errno=errno(*__errno_location ());
779 switch(de->d_type) {
780 case DT_REGDT_REG:
781 /* Skip unless the name ends with '.conf' */
782 if(strcmp((de->d_name + strlen(de->d_name) - 5), ".conf")) {
783 continue;
784 }
785 fname = g_build_filename(dir, de->d_name, NULL((void*)0));
786 tmp = parse_cfile(fname, FALSE(0), e);
787 errno(*__errno_location ())=saved_errno;
788 if(*e) {
789 goto err_out;
790 }
791 if(!retval)
792 retval = g_array_new(FALSE(0), TRUE(!(0)), sizeof(SERVER));
793 retval = g_array_append_vals(retval, tmp->data, tmp->len);
794 g_array_free(tmp, TRUE(!(0)));
795 g_free(fname);
796 default:
797 break;
798 }
799 }
800 if(errno(*__errno_location ())) {
801 g_set_error(e, errdomain, CFILE_READDIR_ERR, "Error trying to read directory: %s", strerror(errno(*__errno_location ())));
802 err_out:
803 if(retval)
804 g_array_free(retval, TRUE(!(0)));
805 return NULL((void*)0);
806 }
807 return retval;
808}
809
810/**
811 * Parse the config file.
812 *
813 * @param f the name of the config file
814 * @param e a GError. @see CFILE_ERRORS for what error values this function can
815 * return.
816 * @return a Array of SERVER* pointers, If the config file is empty or does not
817 * exist, returns an empty GHashTable; if the config file contains an
818 * error, returns NULL, and e is set appropriately
819 **/
820GArray* parse_cfile(gchar* f, bool_Bool have_global, GError** e) {
821 const char* DEFAULT_ERROR = "Could not parse %s in group %s: %s";
822 const char* MISSING_REQUIRED_ERROR = "Could not find required value %s in group %s: %s";
823 gchar* cfdir = NULL((void*)0);
824 SERVER s;
825 gchar *virtstyle=NULL((void*)0);
826 PARAM lp[] = {
827 { "exportname", TRUE(!(0)), PARAM_STRING, &(s.exportname), 0 },
828 { "port", TRUE(!(0)), PARAM_INT, &(s.port), 0 },
829 { "authfile", FALSE(0), PARAM_STRING, &(s.authname), 0 },
830 { "filesize", FALSE(0), PARAM_OFFTPARAM_INT64, &(s.expected_size), 0 },
831 { "virtstyle", FALSE(0), PARAM_STRING, &(virtstyle), 0 },
832 { "prerun", FALSE(0), PARAM_STRING, &(s.prerun), 0 },
833 { "postrun", FALSE(0), PARAM_STRING, &(s.postrun), 0 },
834 { "transactionlog", FALSE(0), PARAM_STRING, &(s.transactionlog), 0 },
835 { "readonly", FALSE(0), PARAM_BOOL, &(s.flags), F_READONLY1 },
836 { "multifile", FALSE(0), PARAM_BOOL, &(s.flags), F_MULTIFILE2 },
837 { "copyonwrite", FALSE(0), PARAM_BOOL, &(s.flags), F_COPYONWRITE4 },
838 { "sparse_cow", FALSE(0), PARAM_BOOL, &(s.flags), F_SPARSE16 },
839 { "sdp", FALSE(0), PARAM_BOOL, &(s.flags), F_SDP32 },
840 { "sync", FALSE(0), PARAM_BOOL, &(s.flags), F_SYNC64 },
841 { "flush", FALSE(0), PARAM_BOOL, &(s.flags), F_FLUSH128 },
842 { "fua", FALSE(0), PARAM_BOOL, &(s.flags), F_FUA256 },
843 { "rotational", FALSE(0), PARAM_BOOL, &(s.flags), F_ROTATIONAL512 },
844 { "temporary", FALSE(0), PARAM_BOOL, &(s.flags), F_TEMPORARY1024 },
845 { "trim", FALSE(0), PARAM_BOOL, &(s.flags), F_TRIM2048 },
846 { "listenaddr", FALSE(0), PARAM_STRING, &(s.listenaddr), 0 },
847 { "maxconnections", FALSE(0), PARAM_INT, &(s.max_connections), 0 },
848 };
849 const int lp_size=sizeof(lp)/sizeof(PARAM);
850 PARAM gp[] = {
851 { "user", FALSE(0), PARAM_STRING, &runuser, 0 },
852 { "group", FALSE(0), PARAM_STRING, &rungroup, 0 },
853 { "oldstyle", FALSE(0), PARAM_BOOL, &do_oldstyle, 1 },
854 { "listenaddr", FALSE(0), PARAM_STRING, &modern_listen, 0 },
855 { "port", FALSE(0), PARAM_STRING, &modernport, 0 },
856 { "includedir", FALSE(0), PARAM_STRING, &cfdir, 0 },
857 };
858 PARAM* p=gp;
859 int p_size=sizeof(gp)/sizeof(PARAM);
860 GKeyFile *cfile;
861 GError *err = NULL((void*)0);
862 const char *err_msg=NULL((void*)0);
863 GQuark errdomain;
864 GArray *retval=NULL((void*)0);
865 gchar **groups;
866 gboolean bval;
867 gint ival;
868 gint64 i64val;
869 gchar* sval;
870 gchar* startgroup;
871 gint i;
872 gint j;
873
874 errdomain = g_quark_from_string("parse_cfile");
875 cfile = g_key_file_new();
876 retval = g_array_new(FALSE(0), TRUE(!(0)), sizeof(SERVER));
877 if(!g_key_file_load_from_file(cfile, f, G_KEY_FILE_KEEP_COMMENTS |
878 G_KEY_FILE_KEEP_TRANSLATIONS, &err)) {
879 g_set_error(e, errdomain, CFILE_NOTFOUND, "Could not open config file %s.", f);
880 g_key_file_free(cfile);
881 return retval;
882 }
883 startgroup = g_key_file_get_start_group(cfile);
884 if((!startgroup || strcmp(startgroup, "generic")) && have_global) {
885 g_set_error(e, errdomain, CFILE_MISSING_GENERIC, "Config file does not contain the [generic] group!");
886 g_key_file_free(cfile);
887 return NULL((void*)0);
888 }
889 groups = g_key_file_get_groups(cfile, NULL((void*)0));
890 for(i=0;groups[i];i++) {
891 memset(&s, '\0', sizeof(SERVER));
892
893 /* After the [generic] group or when we're parsing an include
894 * directory, start parsing exports */
895 if(i==1 || !have_global) {
896 p=lp;
897 p_size=lp_size;
898 if(!do_oldstyle) {
899 lp[1].required = FALSE(0);
900 }
901 }
902 for(j=0;j<p_size;j++) {
903 g_assert(p[j].target != NULL)do { if (p[j].target != ((void*)0)) ; else g_assertion_message_expr
(((gchar*) 0), "nbd-server.c", 903, ((const char*) (__PRETTY_FUNCTION__
)), "p[j].target != NULL"); } while (0)
;
904 g_assert(p[j].ptype==PARAM_INT||p[j].ptype==PARAM_STRING||p[j].ptype==PARAM_BOOL||p[j].ptype==PARAM_INT64)do { if (p[j].ptype==PARAM_INT||p[j].ptype==PARAM_STRING||p[j
].ptype==PARAM_BOOL||p[j].ptype==PARAM_INT64) ; else g_assertion_message_expr
(((gchar*) 0), "nbd-server.c", 904, ((const char*) (__PRETTY_FUNCTION__
)), "p[j].ptype==PARAM_INT||p[j].ptype==PARAM_STRING||p[j].ptype==PARAM_BOOL||p[j].ptype==PARAM_INT64"
); } while (0)
;
905 switch(p[j].ptype) {
906 case PARAM_INT:
907 ival = g_key_file_get_integer(cfile,
908 groups[i],
909 p[j].paramname,
910 &err);
911 if(!err) {
912 *((gint*)p[j].target) = ival;
913 }
914 break;
915 case PARAM_INT64:
916 i64val = g_key_file_get_int64(cfile,
917 groups[i],
918 p[j].paramname,
919 &err);
920 if(!err) {
921 *((gint64*)p[j].target) = i64val;
922 }
923 break;
924 case PARAM_STRING:
925 sval = g_key_file_get_string(cfile,
926 groups[i],
927 p[j].paramname,
928 &err);
929 if(!err) {
930 *((gchar**)p[j].target) = sval;
931 }
932 break;
933 case PARAM_BOOL:
934 bval = g_key_file_get_boolean(cfile,
935 groups[i],
936 p[j].paramname, &err);
937 if(!err) {
938 if(bval) {
939 *((gint*)p[j].target) |= p[j].flagval;
940 } else {
941 *((gint*)p[j].target) &= ~(p[j].flagval);
942 }
943 }
944 break;
945 }
946 if(err) {
947 if(err->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
948 if(!p[j].required) {
949 /* Ignore not-found error for optional values */
950 g_clear_error(&err);
951 continue;
952 } else {
953 err_msg = MISSING_REQUIRED_ERROR;
954 }
955 } else {
956 err_msg = DEFAULT_ERROR;
957 }
958 g_set_error(e, errdomain, CFILE_VALUE_INVALID, err_msg, p[j].paramname, groups[i], err->message);
959 g_array_free(retval, TRUE(!(0)));
960 g_error_free(err);
961 g_key_file_free(cfile);
962 return NULL((void*)0);
963 }
964 }
965 if(virtstyle) {
966 if(!strncmp(virtstyle, "none", 4)) {
967 s.virtstyle=VIRT_NONE;
968 } else if(!strncmp(virtstyle, "ipliteral", 9)) {
969 s.virtstyle=VIRT_IPLIT;
970 } else if(!strncmp(virtstyle, "iphash", 6)) {
971 s.virtstyle=VIRT_IPHASH;
972 } else if(!strncmp(virtstyle, "cidrhash", 8)) {
973 s.virtstyle=VIRT_CIDR;
974 if(strlen(virtstyle)<10) {
975 g_set_error(e, errdomain, CFILE_VALUE_INVALID, "Invalid value %s for parameter virtstyle in group %s: missing length", virtstyle, groups[i]);
976 g_array_free(retval, TRUE(!(0)));
977 g_key_file_free(cfile);
978 return NULL((void*)0);
979 }
980 s.cidrlen=strtol(virtstyle+8, NULL((void*)0), 0);
981 } else {
982 g_set_error(e, errdomain, CFILE_VALUE_INVALID, "Invalid value %s for parameter virtstyle in group %s", virtstyle, groups[i]);
983 g_array_free(retval, TRUE(!(0)));
984 g_key_file_free(cfile);
985 return NULL((void*)0);
986 }
987 } else {
988 s.virtstyle=VIRT_IPLIT;
989 }
990 if(s.port && !do_oldstyle) {
991 g_warning("A port was specified, but oldstyle exports were not requested. This may not do what you expect.")g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "A port was specified, but oldstyle exports were not requested. This may not do what you expect."
)
;
992 g_warning("Please read 'man 5 nbd-server' and search for oldstyle for more info")g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "Please read 'man 5 nbd-server' and search for oldstyle for more info"
)
;
993 }
994 /* Don't need to free this, it's not our string */
995 virtstyle=NULL((void*)0);
996 /* Don't append values for the [generic] group */
997 if(i>0 || !have_global) {
998 s.socket_family = AF_UNSPEC0;
999 s.servename = groups[i];
1000
1001 append_serve(&s, retval);
1002 }
1003#ifndef WITH_SDP
1004 if(s.flags & F_SDP32) {
1005 g_set_error(e, errdomain, CFILE_VALUE_UNSUPPORTED, "This nbd-server was built without support for SDP, yet group %s uses it", groups[i]);
1006 g_array_free(retval, TRUE(!(0)));
1007 g_key_file_free(cfile);
1008 return NULL((void*)0);
1009 }
1010#endif
1011 }
1012 g_key_file_free(cfile);
1013 if(cfdir) {
1014 GArray* extra = do_cfile_dir(cfdir, e);
1015 if(extra) {
1016 retval = g_array_append_vals(retval, extra->data, extra->len);
1017 i+=extra->len;
1018 g_array_free(extra, TRUE(!(0)));
1019 } else {
1020 if(*e) {
1021 g_array_free(retval, TRUE(!(0)));
1022 return NULL((void*)0);
1023 }
1024 }
1025 }
1026 if(i==1 && have_global) {
1027 g_set_error(e, errdomain, CFILE_NO_EXPORTS, "The config file does not specify any exports");
1028 }
1029 return retval;
1030}
1031
1032/**
1033 * Signal handler for SIGCHLD
1034 * @param s the signal we're handling (must be SIGCHLD, or something
1035 * is severely wrong)
1036 **/
1037void sigchld_handler(int s) {
1038 int status;
1039 int* i;
1040 pid_t pid;
1041
1042 while((pid=waitpid(-1, &status, WNOHANG1)) > 0) {
1043 if(WIFEXITED(status)((((__extension__ (((union { __typeof(status) __in; int __i; }
) { .__in = (status) }).__i))) & 0x7f) == 0)
) {
1044 msg3(LOG_INFO, "Child exited with %d", WEXITSTATUS(status))g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"Child exited with %d"
,((((__extension__ (((union { __typeof(status) __in; int __i;
}) { .__in = (status) }).__i))) & 0xff00) >> 8))
;
1045 }
1046 i=g_hash_table_lookup(children, &pid);
1047 if(!i) {
1048 msg3(LOG_INFO, "SIGCHLD received for an unknown child with PID %ld", (long)pid)g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"SIGCHLD received for an unknown child with PID %ld"
,(long)pid)
;
1049 } else {
1050 DEBUG("Removing %d from the list of children", pid);
1051 g_hash_table_remove(children, &pid);
1052 }
1053 }
1054}
1055
1056/**
1057 * Kill a child. Called from sigterm_handler::g_hash_table_foreach.
1058 *
1059 * @param key the key
1060 * @param value the value corresponding to the above key
1061 * @param user_data a pointer which we always set to 1, so that we know what
1062 * will happen next.
1063 **/
1064void killchild(gpointer key, gpointer value, gpointer user_data) {
1065 pid_t *pid=value;
1066 int *parent=user_data;
1067
1068 kill(*pid, SIGTERM15);
1069 *parent=1;
1070}
1071
1072/**
1073 * Handle SIGTERM and dispatch it to our children
1074 * @param s the signal we're handling (must be SIGTERM, or something
1075 * is severely wrong).
1076 **/
1077void sigterm_handler(int s) {
1078 int parent=0;
1079
1080 g_hash_table_foreach(children, killchild, &parent);
1081
1082 if(parent) {
1083 unlink(pidfname);
1084 }
1085
1086 exit(EXIT_SUCCESS0);
1087}
1088
1089/**
1090 * Detect the size of a file.
1091 *
1092 * @param fhandle An open filedescriptor
1093 * @return the size of the file, or OFFT_MAX if detection was
1094 * impossible.
1095 **/
1096off_t size_autodetect(int fhandle) {
1097 off_t es;
1098 u64 bytes __attribute__((unused));
1099 struct stat stat_buf;
1100 int error;
1101
1102#ifdef HAVE_SYS_MOUNT_H1
1103#ifdef HAVE_SYS_IOCTL_H1
1104#ifdef BLKGETSIZE64(((2U) << (((0 +8)+8)+14)) | (((0x12)) << (0 +8))
| (((114)) << 0) | ((((sizeof(size_t)))) << ((0 +
8)+8)))
1105 DEBUG("looking for export size with ioctl BLKGETSIZE64\n");
1106 if (!ioctl(fhandle, BLKGETSIZE64(((2U) << (((0 +8)+8)+14)) | (((0x12)) << (0 +8))
| (((114)) << 0) | ((((sizeof(size_t)))) << ((0 +
8)+8)))
, &bytes) && bytes) {
1107 return (off_t)bytes;
1108 }
1109#endif /* BLKGETSIZE64 */
1110#endif /* HAVE_SYS_IOCTL_H */
1111#endif /* HAVE_SYS_MOUNT_H */
1112
1113 DEBUG("looking for fhandle size with fstat\n");
1114 stat_buf.st_size = 0;
1115 error = fstat(fhandle, &stat_buf);
1116 if (!error) {
1117 /* always believe stat if a regular file as it might really
1118 * be zero length */
1119 if (S_ISREG(stat_buf.st_mode)((((stat_buf.st_mode)) & 0170000) == (0100000)) || (stat_buf.st_size > 0))
1120 return (off_t)stat_buf.st_size;
1121 } else {
1122 err("fstat failed: %m");
1123 }
1124
1125 DEBUG("looking for fhandle size with lseek SEEK_END\n");
1126 es = lseek(fhandle, (off_t)0, SEEK_END2);
1127 if (es > ((off_t)0)) {
1128 return es;
1129 } else {
1130 DEBUG("lseek failed: %d", errno==EBADF?1:(errno==ESPIPE?2:(errno==EINVAL?3:4)));
1131 }
1132
1133 err("Could not find size of exported block device: %m");
1134 return OFFT_MAX~((off_t)1<<(sizeof(off_t)*8 -1));
1135}
1136
1137/**
1138 * Get the file handle and offset, given an export offset.
1139 *
1140 * @param export An array of export files
1141 * @param a The offset to get corresponding file/offset for
1142 * @param fhandle [out] File descriptor
1143 * @param foffset [out] Offset into fhandle
1144 * @param maxbytes [out] Tells how many bytes can be read/written
1145 * from fhandle starting at foffset (0 if there is no limit)
1146 * @return 0 on success, -1 on failure
1147 **/
1148int get_filepos(GArray* export, off_t a, int* fhandle, off_t* foffset, size_t* maxbytes ) {
1149 /* Negative offset not allowed */
1150 if(a < 0)
1151 return -1;
1152
1153 /* Binary search for last file with starting offset <= a */
1154 FILE_INFO fi;
1155 int start = 0;
1156 int end = export->len - 1;
1157 while( start <= end ) {
1158 int mid = (start + end) / 2;
1159 fi = g_array_index(export, FILE_INFO, mid)(((FILE_INFO*) (void *) (export)->data) [(mid)]);
1160 if( fi.startoff < a ) {
1161 start = mid + 1;
1162 } else if( fi.startoff > a ) {
1163 end = mid - 1;
1164 } else {
1165 start = end = mid;
1166 break;
1167 }
1168 }
1169
1170 /* end should never go negative, since first startoff is 0 and a >= 0 */
1171 g_assert(end >= 0)do { if (end >= 0) ; else g_assertion_message_expr (((gchar
*) 0), "nbd-server.c", 1171, ((const char*) (__PRETTY_FUNCTION__
)), "end >= 0"); } while (0)
;
1172
1173 fi = g_array_index(export, FILE_INFO, end)(((FILE_INFO*) (void *) (export)->data) [(end)]);
1174 *fhandle = fi.fhandle;
1175 *foffset = a - fi.startoff;
1176 *maxbytes = 0;
1177 if( end+1 < export->len ) {
1178 FILE_INFO fi_next = g_array_index(export, FILE_INFO, end+1)(((FILE_INFO*) (void *) (export)->data) [(end+1)]);
1179 *maxbytes = fi_next.startoff - a;
1180 }
1181
1182 return 0;
1183}
1184
1185/**
1186 * seek to a position in a file, with error handling.
1187 * @param handle a filedescriptor
1188 * @param a position to seek to
1189 * @todo get rid of this; lastpoint is a global variable right now, but it
1190 * shouldn't be. If we pass it on as a parameter, that makes things a *lot*
1191 * easier.
1192 **/
1193void myseek(int handle,off_t a) {
1194 if (lseek(handle, a, SEEK_SET0) < 0) {
1195 err("Can not seek locally!\n");
1196 }
1197}
1198
1199/**
1200 * Write an amount of bytes at a given offset to the right file. This
1201 * abstracts the write-side of the multiple file option.
1202 *
1203 * @param a The offset where the write should start
1204 * @param buf The buffer to write from
1205 * @param len The length of buf
1206 * @param client The client we're serving for
1207 * @param fua Flag to indicate 'Force Unit Access'
1208 * @return The number of bytes actually written, or -1 in case of an error
1209 **/
1210ssize_t rawexpwrite(off_t a, char *buf, size_t len, CLIENT *client, int fua) {
1211 int fhandle;
1212 off_t foffset;
1213 size_t maxbytes;
1214 ssize_t retval;
1215
1216 if(get_filepos(client->export, a, &fhandle, &foffset, &maxbytes))
1217 return -1;
1218 if(maxbytes && len > maxbytes)
1219 len = maxbytes;
1220
1221 DEBUG("(WRITE to fd %d offset %llu len %u fua %d), ", fhandle, (long long unsigned)foffset, (unsigned int)len, fua);
1222
1223 myseek(fhandle, foffset);
1224 retval = write(fhandle, buf, len);
1225 if(client->server->flags & F_SYNC64) {
1226 fsync(fhandle);
1227 } else if (fua) {
1228
1229 /* This is where we would do the following
1230 * #ifdef USE_SYNC_FILE_RANGE
1231 * However, we don't, for the reasons set out below
1232 * by Christoph Hellwig <hch@infradead.org>
1233 *
1234 * [BEGINS]
1235 * fdatasync is equivalent to fsync except that it does not flush
1236 * non-essential metadata (basically just timestamps in practice), but it
1237 * does flush metadata requried to find the data again, e.g. allocation
1238 * information and extent maps. sync_file_range does nothing but flush
1239 * out pagecache content - it means you basically won't get your data
1240 * back in case of a crash if you either:
1241 *
1242 * a) have a volatile write cache in your disk (e.g. any normal SATA disk)
1243 * b) are using a sparse file on a filesystem
1244 * c) are using a fallocate-preallocated file on a filesystem
1245 * d) use any file on a COW filesystem like btrfs
1246 *
1247 * e.g. it only does anything useful for you if you do not have a volatile
1248 * write cache, and either use a raw block device node, or just overwrite
1249 * an already fully allocated (and not preallocated) file on a non-COW
1250 * filesystem.
1251 * [ENDS]
1252 *
1253 * What we should do is open a second FD with O_DSYNC set, then write to
1254 * that when appropriate. However, with a Linux client, every REQ_FUA
1255 * immediately follows a REQ_FLUSH, so fdatasync does not cause performance
1256 * problems.
1257 *
1258 */
1259#if 0
1260 sync_file_range(fhandle, foffset, len,
1261 SYNC_FILE_RANGE_WAIT_BEFORE1 | SYNC_FILE_RANGE_WRITE2 |
1262 SYNC_FILE_RANGE_WAIT_AFTER4);
1263#else
1264 fdatasync(fhandle);
1265#endif
1266 }
1267 return retval;
1268}
1269
1270/**
1271 * Call rawexpwrite repeatedly until all data has been written.
1272 *
1273 * @param a The offset where the write should start
1274 * @param buf The buffer to write from
1275 * @param len The length of buf
1276 * @param client The client we're serving for
1277 * @param fua Flag to indicate 'Force Unit Access'
1278 * @return 0 on success, nonzero on failure
1279 **/
1280int rawexpwrite_fully(off_t a, char *buf, size_t len, CLIENT *client, int fua) {
1281 ssize_t ret=0;
1282
1283 while(len > 0 && (ret=rawexpwrite(a, buf, len, client, fua)) > 0 ) {
1284 a += ret;
1285 buf += ret;
1286 len -= ret;
1287 }
1288 return (ret < 0 || len != 0);
1289}
1290
1291/**
1292 * Read an amount of bytes at a given offset from the right file. This
1293 * abstracts the read-side of the multiple files option.
1294 *
1295 * @param a The offset where the read should start
1296 * @param buf A buffer to read into
1297 * @param len The size of buf
1298 * @param client The client we're serving for
1299 * @return The number of bytes actually read, or -1 in case of an
1300 * error.
1301 **/
1302ssize_t rawexpread(off_t a, char *buf, size_t len, CLIENT *client) {
1303 int fhandle;
1304 off_t foffset;
1305 size_t maxbytes;
1306
1307 if(get_filepos(client->export, a, &fhandle, &foffset, &maxbytes))
1308 return -1;
1309 if(maxbytes && len > maxbytes)
1310 len = maxbytes;
1311
1312 DEBUG("(READ from fd %d offset %llu len %u), ", fhandle, (long long unsigned int)foffset, (unsigned int)len);
1313
1314 myseek(fhandle, foffset);
1315 return read(fhandle, buf, len);
1316}
1317
1318/**
1319 * Call rawexpread repeatedly until all data has been read.
1320 * @return 0 on success, nonzero on failure
1321 **/
1322int rawexpread_fully(off_t a, char *buf, size_t len, CLIENT *client) {
1323 ssize_t ret=0;
1324
1325 while(len > 0 && (ret=rawexpread(a, buf, len, client)) > 0 ) {
1326 a += ret;
1327 buf += ret;
1328 len -= ret;
1329 }
1330 return (ret < 0 || len != 0);
1331}
1332
1333/**
1334 * Read an amount of bytes at a given offset from the right file. This
1335 * abstracts the read-side of the copyonwrite stuff, and calls
1336 * rawexpread() with the right parameters to do the actual work.
1337 * @param a The offset where the read should start
1338 * @param buf A buffer to read into
1339 * @param len The size of buf
1340 * @param client The client we're going to read for
1341 * @return 0 on success, nonzero on failure
1342 **/
1343int expread(off_t a, char *buf, size_t len, CLIENT *client) {
1344 off_t rdlen, offset;
1345 off_t mapcnt, mapl, maph, pagestart;
1346
1347 if (!(client->server->flags & F_COPYONWRITE4))
1348 return(rawexpread_fully(a, buf, len, client));
1349 DEBUG("Asked to read %u bytes at %llu.\n", (unsigned int)len, (unsigned long long)a);
1350
1351 mapl=a/DIFFPAGESIZE4096; maph=(a+len-1)/DIFFPAGESIZE4096;
1352
1353 for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
1354 pagestart=mapcnt*DIFFPAGESIZE4096;
1355 offset=a-pagestart;
1356 rdlen=(0<DIFFPAGESIZE4096-offset && len<(size_t)(DIFFPAGESIZE4096-offset)) ?
1357 len : (size_t)DIFFPAGESIZE4096-offset;
1358 if (client->difmap[mapcnt]!=(u32)(-1)) { /* the block is already there */
1359 DEBUG("Page %llu is at %lu\n", (unsigned long long)mapcnt,
1360 (unsigned long)(client->difmap[mapcnt]));
1361 myseek(client->difffile, client->difmap[mapcnt]*DIFFPAGESIZE4096+offset);
1362 if (read(client->difffile, buf, rdlen) != rdlen) return -1;
1363 } else { /* the block is not there */
1364 DEBUG("Page %llu is not here, we read the original one\n",
1365 (unsigned long long)mapcnt);
1366 if(rawexpread_fully(a, buf, rdlen, client)) return -1;
1367 }
1368 len-=rdlen; a+=rdlen; buf+=rdlen;
1369 }
1370 return 0;
1371}
1372
1373/**
1374 * Write an amount of bytes at a given offset to the right file. This
1375 * abstracts the write-side of the copyonwrite option, and calls
1376 * rawexpwrite() with the right parameters to do the actual work.
1377 *
1378 * @param a The offset where the write should start
1379 * @param buf The buffer to write from
1380 * @param len The length of buf
1381 * @param client The client we're going to write for.
1382 * @param fua Flag to indicate 'Force Unit Access'
1383 * @return 0 on success, nonzero on failure
1384 **/
1385int expwrite(off_t a, char *buf, size_t len, CLIENT *client, int fua) {
1386 char pagebuf[DIFFPAGESIZE4096];
1387 off_t mapcnt,mapl,maph;
1388 off_t wrlen,rdlen;
1389 off_t pagestart;
1390 off_t offset;
1391
1392 if (!(client->server->flags & F_COPYONWRITE4))
1393 return(rawexpwrite_fully(a, buf, len, client, fua));
1394 DEBUG("Asked to write %u bytes at %llu.\n", (unsigned int)len, (unsigned long long)a);
1395
1396 mapl=a/DIFFPAGESIZE4096 ; maph=(a+len-1)/DIFFPAGESIZE4096 ;
1397
1398 for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
1399 pagestart=mapcnt*DIFFPAGESIZE4096 ;
1400 offset=a-pagestart ;
1401 wrlen=(0<DIFFPAGESIZE4096-offset && len<(size_t)(DIFFPAGESIZE4096-offset)) ?
1402 len : (size_t)DIFFPAGESIZE4096-offset;
1403
1404 if (client->difmap[mapcnt]!=(u32)(-1)) { /* the block is already there */
1405 DEBUG("Page %llu is at %lu\n", (unsigned long long)mapcnt,
1406 (unsigned long)(client->difmap[mapcnt])) ;
1407 myseek(client->difffile,
1408 client->difmap[mapcnt]*DIFFPAGESIZE4096+offset);
1409 if (write(client->difffile, buf, wrlen) != wrlen) return -1 ;
1410 } else { /* the block is not there */
1411 myseek(client->difffile,client->difffilelen*DIFFPAGESIZE4096) ;
1412 client->difmap[mapcnt]=(client->server->flags&F_SPARSE16)?mapcnt:client->difffilelen++;
1413 DEBUG("Page %llu is not here, we put it at %lu\n",
1414 (unsigned long long)mapcnt,
1415 (unsigned long)(client->difmap[mapcnt]));
1416 rdlen=DIFFPAGESIZE4096 ;
1417 if (rawexpread_fully(pagestart, pagebuf, rdlen, client))
1418 return -1;
1419 memcpy(pagebuf+offset,buf,wrlen) ;
1420 if (write(client->difffile, pagebuf, DIFFPAGESIZE4096) !=
1421 DIFFPAGESIZE4096)
1422 return -1;
1423 }
1424 len-=wrlen ; a+=wrlen ; buf+=wrlen ;
1425 }
1426 if (client->server->flags & F_SYNC64) {
1427 fsync(client->difffile);
1428 } else if (fua) {
1429 /* open question: would it be cheaper to do multiple sync_file_ranges?
1430 as we iterate through the above?
1431 */
1432 fdatasync(client->difffile);
1433 }
1434 return 0;
1435}
1436
1437/**
1438 * Flush data to a client
1439 *
1440 * @param client The client we're going to write for.
1441 * @return 0 on success, nonzero on failure
1442 **/
1443int expflush(CLIENT *client) {
1444 gint i;
1445
1446 if (client->server->flags & F_COPYONWRITE4) {
1447 return fsync(client->difffile);
1448 }
1449
1450 for (i = 0; i < client->export->len; i++) {
1451 FILE_INFO fi = g_array_index(client->export, FILE_INFO, i)(((FILE_INFO*) (void *) (client->export)->data) [(i)]);
1452 if (fsync(fi.fhandle) < 0)
1453 return -1;
1454 }
1455
1456 return 0;
1457}
1458
1459/*
1460 * If the current system supports it, call fallocate() on the backend
1461 * file to resparsify stuff that isn't needed anymore (see NBD_CMD_TRIM)
1462 */
1463int exptrim(struct nbd_request* req, CLIENT* client) {
1464#ifdef HAVE_FALLOC_PH1
1465 FILE_INFO prev = g_array_index(client->export, FILE_INFO, 0)(((FILE_INFO*) (void *) (client->export)->data) [(0)]);
1466 FILE_INFO cur = prev;
1467 int i = 1;
1468 /* We're running on a system that supports the
1469 * FALLOC_FL_PUNCH_HOLE option to re-sparsify a file */
1470 do {
1471 if(i<client->export->len) {
1472 cur = g_array_index(client->export, FILE_INFO, i)(((FILE_INFO*) (void *) (client->export)->data) [(i)]);
1473 }
1474 if(prev.startoff < req->from) {
1475 off_t curoff = req->from - prev.startoff;
1476 off_t curlen = cur.startoff - prev.startoff - curoff;
1477 fallocate(prev.fhandle, FALLOC_FL_PUNCH_HOLE0x02, curoff, curlen);
1478 }
1479 prev = cur;
1480 } while(i < client->export->len && cur.startoff < (req->from + req->len));
1481 DEBUG("Performed TRIM request from %llu to %llu", (unsigned long long) req->from, (unsigned long long) req->len);
1482#else
1483 DEBUG("Ignoring TRIM request (not supported on current platform");
1484#endif
1485 return 0;
1486}
1487
1488/**
1489 * Do the initial negotiation.
1490 *
1491 * @param client The client we're negotiating with.
1492 **/
1493CLIENT* negotiate(int net, CLIENT *client, GArray* servers, int phase) {
1494 char zeros[128];
1495 uint64_t size_host;
1496 uint32_t flags = NBD_FLAG_HAS_FLAGS(1 << 0);
1497 uint16_t smallflags = 0;
1498 uint64_t magic;
1499
1500 memset(zeros, '\0', sizeof(zeros));
1501 if(phase & NEG_INIT(1 << 0)) {
1
Taking true branch
1502 /* common */
1503 if (write(net, INIT_PASSWD"NBDMAGIC", 8) < 0) {
2
Taking true branch
1504 err_nonfatal("Negotiation failed/1: %m");
1505 if(client)
3
Assuming 'client' is null
4
Taking false branch
1506 exit(EXIT_FAILURE1);
1507 }
1508 if(phase & NEG_MODERN(1 << 2)) {
5
Taking false branch
1509 /* modern */
1510 magic = htonllntohll(opts_magic);
1511 } else {
1512 /* oldstyle */
1513 magic = htonllntohll(cliserv_magic);
1514 }
1515 if (write(net, &magic, sizeof(magic)) < 0) {
6
Taking false branch
1516 err_nonfatal("Negotiation failed/2: %m");
1517 if(phase & NEG_OLD(1 << 1))
1518 exit(EXIT_FAILURE1);
1519 }
1520 }
1521 if ((phase & NEG_MODERN(1 << 2)) && (phase & NEG_INIT(1 << 0))) {
7
Taking false branch
1522 /* modern */
1523 uint32_t reserved;
1524 uint32_t opt;
1525 uint32_t namelen;
1526 char* name;
1527 int i;
1528
1529 if(!servers)
1530 err("programmer error");
1531 if (write(net, &smallflags, sizeof(uint16_t)) < 0)
1532 err("Negotiation failed/3: %m");
1533 if (read(net, &reserved, sizeof(reserved)) < 0)
1534 err("Negotiation failed/4: %m");
1535 if (read(net, &magic, sizeof(magic)) < 0)
1536 err("Negotiation failed/5: %m");
1537 magic = ntohll(magic);
1538 if(magic != opts_magic) {
1539 close(net);
1540 return NULL((void*)0);
1541 }
1542 if (read(net, &opt, sizeof(opt)) < 0)
1543 err("Negotiation failed/6: %m");
1544 opt = ntohl(opt);
1545 if(opt != NBD_OPT_EXPORT_NAME(1 << 0)) {
1546 close(net);
1547 return NULL((void*)0);
1548 }
1549 if (read(net, &namelen, sizeof(namelen)) < 0)
1550 err("Negotiation failed/7: %m");
1551 namelen = ntohl(namelen);
1552 name = malloc(namelen+1);
1553 name[namelen]=0;
1554 if (read(net, name, namelen) < 0)
1555 err("Negotiation failed/8: %m");
1556 for(i=0; i<servers->len; i++) {
1557 SERVER* serve = &(g_array_index(servers, SERVER, i)(((SERVER*) (void *) (servers)->data) [(i)]));
1558 if(!strcmp(serve->servename, name)) {
1559 CLIENT* client = g_new0(CLIENT, 1)((CLIENT *) g_malloc0_n ((1), sizeof (CLIENT)));
1560 client->server = serve;
1561 client->exportsize = OFFT_MAX~((off_t)1<<(sizeof(off_t)*8 -1));
1562 client->net = net;
1563 client->modern = TRUE(!(0));
1564 client->transactionlogfd = -1;
1565 free(name);
1566 return client;
1567 }
1568 }
1569 free(name);
1570 return NULL((void*)0);
1571 }
1572 /* common */
1573 size_host = htonllntohll((u64)(client->exportsize));
8
Access to field 'exportsize' results in a dereference of a null pointer (loaded from variable 'client')
1574 if (write(net, &size_host, 8) < 0)
1575 err("Negotiation failed/9: %m");
1576 if (client->server->flags & F_READONLY1)
1577 flags |= NBD_FLAG_READ_ONLY(1 << 1);
1578 if (client->server->flags & F_FLUSH128)
1579 flags |= NBD_FLAG_SEND_FLUSH(1 << 2);
1580 if (client->server->flags & F_FUA256)
1581 flags |= NBD_FLAG_SEND_FUA(1 << 3);
1582 if (client->server->flags & F_ROTATIONAL512)
1583 flags |= NBD_FLAG_ROTATIONAL(1 << 4);
1584 if (client->server->flags & F_TRIM2048)
1585 flags |= NBD_FLAG_SEND_TRIM(1 << 5);
1586 if (phase & NEG_OLD(1 << 1)) {
1587 /* oldstyle */
1588 flags = htonl(flags);
1589 if (write(client->net, &flags, 4) < 0)
1590 err("Negotiation failed/10: %m");
1591 } else {
1592 /* modern */
1593 smallflags = (uint16_t)(flags & ~((uint16_t)0));
1594 smallflags = htons(smallflags);
1595 if (write(client->net, &smallflags, sizeof(smallflags)) < 0) {
1596 err("Negotiation failed/11: %m");
1597 }
1598 }
1599 /* common */
1600 if (write(client->net, zeros, 124) < 0)
1601 err("Negotiation failed/12: %m");
1602 return NULL((void*)0);
1603}
1604
1605/** sending macro. */
1606#define SEND(net,reply){ writeit( net, &reply, sizeof( reply )); if (client->
transactionlogfd != -1) writeit(client->transactionlogfd, &
reply, sizeof(reply)); }
{ writeit( net, &reply, sizeof( reply )); \
1607 if (client->transactionlogfd != -1) \
1608 writeit(client->transactionlogfd, &reply, sizeof(reply)); }
1609/** error macro. */
1610#define ERROR(client,reply,errcode){ reply.error = htonl(errcode); { writeit( client->net, &
reply, sizeof( reply )); if (client->transactionlogfd != -
1) writeit(client->transactionlogfd, &reply, sizeof(reply
)); }; reply.error = 0; }
{ reply.error = htonl(errcode); SEND(client->net,reply){ writeit( client->net, &reply, sizeof( reply )); if (
client->transactionlogfd != -1) writeit(client->transactionlogfd
, &reply, sizeof(reply)); }
; reply.error = 0; }
1611/**
1612 * Serve a file to a single client.
1613 *
1614 * @todo This beast needs to be split up in many tiny little manageable
1615 * pieces. Preferably with a chainsaw.
1616 *
1617 * @param client The client we're going to serve to.
1618 * @return when the client disconnects
1619 **/
1620int mainloop(CLIENT *client) {
1621 struct nbd_request request;
1622 struct nbd_reply reply;
1623 gboolean go_on=TRUE(!(0));
1624#ifdef DODBG
1625 int i = 0;
1626#endif
1627 negotiate(client->net, client, NULL((void*)0), client->modern ? NEG_MODERN(1 << 2) : (NEG_OLD(1 << 1) | NEG_INIT(1 << 0)));
1628 DEBUG("Entering request loop!\n");
1629 reply.magic = htonl(NBD_REPLY_MAGIC0x67446698);
1630 reply.error = 0;
1631 while (go_on) {
1632 char buf[BUFSIZE((1024*1024)+sizeof(struct nbd_reply))];
1633 char* p;
1634 size_t len;
1635 size_t currlen;
1636 size_t writelen;
1637 uint16_t command;
1638#ifdef DODBG
1639 i++;
1640 printf("%d: ", i);
1641#endif
1642 readit(client->net, &request, sizeof(request));
1643 if (client->transactionlogfd != -1)
1644 writeit(client->transactionlogfd, &request, sizeof(request));
1645
1646 request.from = ntohll(request.from);
1647 request.type = ntohl(request.type);
1648 command = request.type & NBD_CMD_MASK_COMMAND0x0000ffff;
1649 len = ntohl(request.len);
1650
1651 DEBUG("%s from %llu (%llu) len %d, ", getcommandname(command),
1652 (unsigned long long)request.from,
1653 (unsigned long long)request.from / 512, (unsigned int)len);
1654
1655 if (request.magic != htonl(NBD_REQUEST_MAGIC0x25609513))
1656 err("Not enough magic.");
1657
1658 memcpy(reply.handle, request.handle, sizeof(reply.handle));
1659
1660 if ((command==NBD_CMD_WRITE) || (command==NBD_CMD_READ)) {
1661 if ((request.from + len) > (OFFT_MAX~((off_t)1<<(sizeof(off_t)*8 -1)))) {
1662 DEBUG("[Number too large!]");
1663 ERROR(client, reply, EINVAL){ reply.error = htonl(22); { writeit( client->net, &reply
, sizeof( reply )); if (client->transactionlogfd != -1) writeit
(client->transactionlogfd, &reply, sizeof(reply)); }; reply
.error = 0; }
;
1664 continue;
1665 }
1666
1667 if (((ssize_t)((off_t)request.from + len) > client->exportsize)) {
1668 DEBUG("[RANGE!]");
1669 ERROR(client, reply, EINVAL){ reply.error = htonl(22); { writeit( client->net, &reply
, sizeof( reply )); if (client->transactionlogfd != -1) writeit
(client->transactionlogfd, &reply, sizeof(reply)); }; reply
.error = 0; }
;
1670 continue;
1671 }
1672
1673 currlen = len;
1674 if (currlen > BUFSIZE((1024*1024)+sizeof(struct nbd_reply)) - sizeof(struct nbd_reply)) {
1675 currlen = BUFSIZE((1024*1024)+sizeof(struct nbd_reply)) - sizeof(struct nbd_reply);
1676 if(!logged_oversized) {
1677 msg2(LOG_DEBUG, "oversized request (this is not a problem)")g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"oversized request (this is not a problem)"
)
;
1678 logged_oversized = true1;
1679 }
1680 }
1681 }
1682
1683 switch (command) {
1684
1685 case NBD_CMD_DISC:
1686 msg2(LOG_INFO, "Disconnect request received.")g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"Disconnect request received."
)
;
1687 if (client->server->flags & F_COPYONWRITE4) {
1688 if (client->difmap) g_free(client->difmap) ;
1689 close(client->difffile);
1690 unlink(client->difffilename);
1691 free(client->difffilename);
1692 }
1693 go_on=FALSE(0);
1694 continue;
1695
1696 case NBD_CMD_WRITE:
1697 DEBUG("wr: net->buf, ");
1698 while(len > 0) {
1699 readit(client->net, buf, currlen);
1700 DEBUG("buf->exp, ");
1701 if ((client->server->flags & F_READONLY1) ||
1702 (client->server->flags & F_AUTOREADONLY8)) {
1703 DEBUG("[WRITE to READONLY!]");
1704 ERROR(client, reply, EPERM){ reply.error = htonl(1); { writeit( client->net, &reply
, sizeof( reply )); if (client->transactionlogfd != -1) writeit
(client->transactionlogfd, &reply, sizeof(reply)); }; reply
.error = 0; }
;
1705 consume(client->net, buf, len-currlen, BUFSIZE((1024*1024)+sizeof(struct nbd_reply)));
1706 continue;
1707 }
1708 if (expwrite(request.from, buf, currlen, client,
1709 request.type & NBD_CMD_FLAG_FUA(1<<16))) {
1710 DEBUG("Write failed: %m" );
1711 ERROR(client, reply, errno){ reply.error = htonl((*__errno_location ())); { writeit( client
->net, &reply, sizeof( reply )); if (client->transactionlogfd
!= -1) writeit(client->transactionlogfd, &reply, sizeof
(reply)); }; reply.error = 0; }
;
1712 consume(client->net, buf, len-currlen, BUFSIZE((1024*1024)+sizeof(struct nbd_reply)));
1713 continue;
1714 }
1715 len -= currlen;
1716 request.from += currlen;
1717 currlen = (len < BUFSIZE((1024*1024)+sizeof(struct nbd_reply))) ? len : BUFSIZE((1024*1024)+sizeof(struct nbd_reply));
1718 }
1719 SEND(client->net, reply){ writeit( client->net, &reply, sizeof( reply )); if (
client->transactionlogfd != -1) writeit(client->transactionlogfd
, &reply, sizeof(reply)); }
;
1720 DEBUG("OK!\n");
1721 continue;
1722
1723 case NBD_CMD_FLUSH:
1724 DEBUG("fl: ");
1725 if (expflush(client)) {
1726 DEBUG("Flush failed: %m");
1727 ERROR(client, reply, errno){ reply.error = htonl((*__errno_location ())); { writeit( client
->net, &reply, sizeof( reply )); if (client->transactionlogfd
!= -1) writeit(client->transactionlogfd, &reply, sizeof
(reply)); }; reply.error = 0; }
;
1728 continue;
1729 }
1730 SEND(client->net, reply){ writeit( client->net, &reply, sizeof( reply )); if (
client->transactionlogfd != -1) writeit(client->transactionlogfd
, &reply, sizeof(reply)); }
;
1731 DEBUG("OK!\n");
1732 continue;
1733
1734 case NBD_CMD_READ:
1735 DEBUG("exp->buf, ");
1736 memcpy(buf, &reply, sizeof(struct nbd_reply));
1737 if (client->transactionlogfd != -1)
1738 writeit(client->transactionlogfd, &reply, sizeof(reply));
1739 p = buf + sizeof(struct nbd_reply);
1740 writelen = currlen + sizeof(struct nbd_reply);
1741 while(len > 0) {
1742 if (expread(request.from, p, currlen, client)) {
1743 DEBUG("Read failed: %m");
1744 ERROR(client, reply, errno){ reply.error = htonl((*__errno_location ())); { writeit( client
->net, &reply, sizeof( reply )); if (client->transactionlogfd
!= -1) writeit(client->transactionlogfd, &reply, sizeof
(reply)); }; reply.error = 0; }
;
1745 continue;
1746 }
1747
1748 DEBUG("buf->net, ");
1749 writeit(client->net, buf, writelen);
1750 len -= currlen;
1751 request.from += currlen;
1752 currlen = (len < BUFSIZE((1024*1024)+sizeof(struct nbd_reply))) ? len : BUFSIZE((1024*1024)+sizeof(struct nbd_reply));
1753 p = buf;
1754 writelen = currlen;
1755 }
1756 DEBUG("OK!\n");
1757 continue;
1758
1759 case NBD_CMD_TRIM:
1760 /* The kernel module sets discard_zeroes_data == 0,
1761 * so it is okay to do nothing. */
1762 if (exptrim(&request, client)) {
1763 DEBUG("Trim failed: %m");
1764 ERROR(client, reply, errno){ reply.error = htonl((*__errno_location ())); { writeit( client
->net, &reply, sizeof( reply )); if (client->transactionlogfd
!= -1) writeit(client->transactionlogfd, &reply, sizeof
(reply)); }; reply.error = 0; }
;
1765 continue;
1766 }
1767 SEND(client->net, reply){ writeit( client->net, &reply, sizeof( reply )); if (
client->transactionlogfd != -1) writeit(client->transactionlogfd
, &reply, sizeof(reply)); }
;
1768 continue;
1769
1770 default:
1771 DEBUG ("Ignoring unknown command\n");
1772 continue;
1773 }
1774 }
1775 return 0;
1776}
1777
1778/**
1779 * Set up client export array, which is an array of FILE_INFO.
1780 * Also, split a single exportfile into multiple ones, if that was asked.
1781 * @param client information on the client which we want to setup export for
1782 **/
1783void setupexport(CLIENT* client) {
1784 int i;
1785 off_t laststartoff = 0, lastsize = 0;
1786 int multifile = (client->server->flags & F_MULTIFILE2);
1787 int temporary = (client->server->flags & F_TEMPORARY1024) && !multifile;
1788 int cancreate = (client->server->expected_size) && !multifile;
1789
1790 client->export = g_array_new(TRUE(!(0)), TRUE(!(0)), sizeof(FILE_INFO));
1791
1792 /* If multi-file, open as many files as we can.
1793 * If not, open exactly one file.
1794 * Calculate file sizes as we go to get total size. */
1795 for(i=0; ; i++) {
1796 FILE_INFO fi;
1797 gchar *tmpname;
1798 gchar* error_string;
1799
1800 if (i)
1801 cancreate = 0;
1802 /* if expected_size is specified, and this is the first file, we can create the file */
1803 mode_t mode = (client->server->flags & F_READONLY1) ?
1804 O_RDONLY00 : (O_RDWR02 | (cancreate?O_CREAT0100:0));
1805
1806 if (temporary) {
1807 tmpname=g_strdup_printf("%s.%d-XXXXXX", client->exportname, i);
1808 DEBUG( "Opening %s\n", tmpname );
1809 fi.fhandle = mkstemp(tmpname);
1810 } else {
1811 if(multifile) {
1812 tmpname=g_strdup_printf("%s.%d", client->exportname, i);
1813 } else {
1814 tmpname=g_strdup(client->exportname);
1815 }
1816 DEBUG( "Opening %s\n", tmpname );
1817 fi.fhandle = open(tmpname, mode, 0x600);
1818 if(fi.fhandle == -1 && mode == O_RDWR02) {
1819 /* Try again because maybe media was read-only */
1820 fi.fhandle = open(tmpname, O_RDONLY00);
1821 if(fi.fhandle != -1) {
1822 /* Opening the base file in copyonwrite mode is
1823 * okay */
1824 if(!(client->server->flags & F_COPYONWRITE4)) {
1825 client->server->flags |= F_AUTOREADONLY8;
1826 client->server->flags |= F_READONLY1;
1827 }
1828 }
1829 }
1830 }
1831 if(fi.fhandle == -1) {
1832 if(multifile && i>0)
1833 break;
1834 error_string=g_strdup_printf(
1835 "Could not open exported file %s: %%m",
1836 tmpname);
1837 err(error_string);
1838 }
1839
1840 if (temporary)
1841 unlink(tmpname); /* File will stick around whilst FD open */
1842
1843 fi.startoff = laststartoff + lastsize;
1844 g_array_append_val(client->export, fi)g_array_append_vals (client->export, &(fi), 1);
1845 g_free(tmpname);
1846
1847 /* Starting offset and size of this file will be used to
1848 * calculate starting offset of next file */
1849 laststartoff = fi.startoff;
1850 lastsize = size_autodetect(fi.fhandle);
1851
1852 /* If we created the file, it will be length zero */
1853 if (!lastsize && cancreate) {
1854 /* we can ignore errors as we recalculate the size */
1855 ftruncate (fi.fhandle, client->server->expected_size);
1856 lastsize = size_autodetect(fi.fhandle);
1857 if (lastsize != client->server->expected_size)
1858 err("Could not expand file");
1859 break; /* don't look for any more files */
1860 }
1861
1862 if(!multifile || temporary)
1863 break;
1864 }
1865
1866 /* Set export size to total calculated size */
1867 client->exportsize = laststartoff + lastsize;
1868
1869 /* Export size may be overridden */
1870 if(client->server->expected_size) {
1871 /* desired size must be <= total calculated size */
1872 if(client->server->expected_size > client->exportsize) {
1873 err("Size of exported file is too big\n");
1874 }
1875
1876 client->exportsize = client->server->expected_size;
1877 }
1878
1879 msg3(LOG_INFO, "Size of exported file/device is %llu", (unsigned long long)client->exportsize)g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"Size of exported file/device is %llu"
,(unsigned long long)client->exportsize)
;
1880 if(multifile) {
1881 msg3(LOG_INFO, "Total number of files: %d", i)g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"Total number of files: %d"
,i)
;
1882 }
1883}
1884
1885int copyonwrite_prepare(CLIENT* client) {
1886 off_t i;
1887 if ((client->difffilename = malloc(1024))==NULL((void*)0))
1888 err("Failed to allocate string for diff file name");
1889 snprintf(client->difffilename, 1024, "%s-%s-%d.diff",client->exportname,client->clientname,
1890 (int)getpid()) ;
1891 client->difffilename[1023]='\0';
1892 msg3(LOG_INFO,"About to create map and diff file %s",client->difffilename)g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"About to create map and diff file %s"
,client->difffilename)
;
1893 client->difffile=open(client->difffilename,O_RDWR02 | O_CREAT0100 | O_TRUNC01000,0600) ;
1894 if (client->difffile<0) err("Could not create diff file (%m)") ;
1895 if ((client->difmap=calloc(client->exportsize/DIFFPAGESIZE4096,sizeof(u32)))==NULL((void*)0))
1896 err("Could not allocate memory") ;
1897 for (i=0;i<client->exportsize/DIFFPAGESIZE4096;i++) client->difmap[i]=(u32)-1 ;
1898
1899 return 0;
1900}
1901
1902/**
1903 * Run a command. This is used for the ``prerun'' and ``postrun'' config file
1904 * options
1905 *
1906 * @param command the command to be ran. Read from the config file
1907 * @param file the file name we're about to export
1908 **/
1909int do_run(gchar* command, gchar* file) {
1910 gchar* cmd;
1911 int retval=0;
1912
1913 if(command && *command) {
1914 cmd = g_strdup_printf(command, file);
1915 retval=system(cmd);
1916 g_free(cmd);
1917 }
1918 return retval;
1919}
1920
1921/**
1922 * Serve a connection.
1923 *
1924 * @todo allow for multithreading, perhaps use libevent. Not just yet, though;
1925 * follow the road map.
1926 *
1927 * @param client a connected client
1928 **/
1929void serveconnection(CLIENT *client) {
1930 if (client->server->transactionlog && (client->transactionlogfd == -1))
1931 {
1932 if (-1 == (client->transactionlogfd = open(client->server->transactionlog,
1933 O_WRONLY01 | O_CREAT0100,
1934 S_IRUSR0400 | S_IWUSR0200)))
1935 g_warning("Could not open transaction log %s",g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "Could not open transaction log %s"
, client->server->transactionlog)
1936 client->server->transactionlog)g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "Could not open transaction log %s"
, client->server->transactionlog)
;
1937 }
1938
1939 if(do_run(client->server->prerun, client->exportname)) {
1940 exit(EXIT_FAILURE1);
1941 }
1942 setupexport(client);
1943
1944 if (client->server->flags & F_COPYONWRITE4) {
1945 copyonwrite_prepare(client);
1946 }
1947
1948 setmysockopt(client->net);
1949
1950 mainloop(client);
1951 do_run(client->server->postrun, client->exportname);
1952
1953 if (-1 != client->transactionlogfd)
1954 {
1955 close(client->transactionlogfd);
1956 client->transactionlogfd = -1;
1957 }
1958}
1959
1960/**
1961 * Find the name of the file we have to serve. This will use g_strdup_printf
1962 * to put the IP address of the client inside a filename containing
1963 * "%s" (in the form as specified by the "virtstyle" option). That name
1964 * is then written to client->exportname.
1965 *
1966 * @param net A socket connected to an nbd client
1967 * @param client information about the client. The IP address in human-readable
1968 * format will be written to a new char* buffer, the address of which will be
1969 * stored in client->clientname.
1970 **/
1971void set_peername(int net, CLIENT *client) {
1972 struct sockaddr_storage addrin;
1973 struct sockaddr_storage netaddr;
1974 struct sockaddr_in *netaddr4 = NULL((void*)0);
1975 struct sockaddr_in6 *netaddr6 = NULL((void*)0);
1976 socklen_t addrinlen = sizeof( addrin );
1977 struct addrinfo hints;
1978 struct addrinfo *ai = NULL((void*)0);
1979 char peername[NI_MAXHOST1025];
1980 char netname[NI_MAXHOST1025];
1981 char *tmp = NULL((void*)0);
1982 int i;
1983 int e;
1984 int shift;
1985
1986 if (getpeername(net, (struct sockaddr *) &addrin, &addrinlen) < 0)
1987 err("getpeername failed: %m");
1988
1989 if((e = getnameinfo((struct sockaddr *)&addrin, addrinlen,
1990 peername, sizeof (peername), NULL((void*)0), 0, NI_NUMERICHOST1))) {
1991 msg3(LOG_INFO, "getnameinfo failed: %s", gai_strerror(e))g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"getnameinfo failed: %s"
,gai_strerror(e))
;
1992 freeaddrinfo(ai);
1993 }
1994
1995 memset(&hints, '\0', sizeof (hints));
1996 hints.ai_flags = AI_ADDRCONFIG0x0020;
1997 e = getaddrinfo(peername, NULL((void*)0), &hints, &ai);
1998
1999 if(e != 0) {
2000 msg3(LOG_INFO, "getaddrinfo failed: %s", gai_strerror(e))g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"getaddrinfo failed: %s"
,gai_strerror(e))
;
2001 freeaddrinfo(ai);
2002 return;
2003 }
2004
2005 switch(client->server->virtstyle) {
2006 case VIRT_NONE:
2007 client->exportname=g_strdup(client->server->exportname);
2008 break;
2009 case VIRT_IPHASH:
2010 for(i=0;i<strlen(peername);i++) {
2011 if(peername[i]=='.') {
2012 peername[i]='/';
2013 }
2014 }
2015 case VIRT_IPLIT:
2016 client->exportname=g_strdup_printf(client->server->exportname, peername);
2017 break;
2018 case VIRT_CIDR:
2019 memcpy(&netaddr, &addrin, addrinlen);
2020 if(ai->ai_family == AF_INET2) {
2021 netaddr4 = (struct sockaddr_in *)&netaddr;
2022 (netaddr4->sin_addr).s_addr>>=32-(client->server->cidrlen);
2023 (netaddr4->sin_addr).s_addr<<=32-(client->server->cidrlen);
2024
2025 getnameinfo((struct sockaddr *) netaddr4, addrinlen,
2026 netname, sizeof (netname), NULL((void*)0), 0, NI_NUMERICHOST1);
2027 tmp=g_strdup_printf("%s/%s", netname, peername);
2028 }else if(ai->ai_family == AF_INET610) {
2029 netaddr6 = (struct sockaddr_in6 *)&netaddr;
2030
2031 shift = 128-(client->server->cidrlen);
2032 i = 3;
2033 while(shift >= 8) {
2034 ((netaddr6->sin6_addr).s6_addr__in6_u.__u6_addr8[i])=0;
2035 shift-=8;
2036 i--;
2037 }
2038 (netaddr6->sin6_addr).s6_addr__in6_u.__u6_addr8[i]>>=shift;
2039 (netaddr6->sin6_addr).s6_addr__in6_u.__u6_addr8[i]<<=shift;
2040
2041 getnameinfo((struct sockaddr *)netaddr6, addrinlen,
2042 netname, sizeof(netname), NULL((void*)0), 0, NI_NUMERICHOST1);
2043 tmp=g_strdup_printf("%s/%s", netname, peername);
2044 }
2045
2046 if(tmp != NULL((void*)0))
2047 client->exportname=g_strdup_printf(client->server->exportname, tmp);
2048
2049 break;
2050 }
2051
2052 freeaddrinfo(ai);
2053 msg4(LOG_INFO, "connect from %s, assigned file is %s",g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"connect from %s, assigned file is %s"
,peername,client->exportname)
2054 peername, client->exportname)g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"connect from %s, assigned file is %s"
,peername,client->exportname)
;
2055 client->clientname=g_strdup(peername);
2056}
2057
2058/**
2059 * Destroy a pid_t*
2060 * @param data a pointer to pid_t which should be freed
2061 **/
2062void destroy_pid_t(gpointer data) {
2063 g_free(data);
2064}
2065
2066/**
2067 * Loop through the available servers, and serve them. Never returns.
2068 **/
2069int serveloop(GArray* servers) {
2070 struct sockaddr_storage addrin;
2071 socklen_t addrinlen=sizeof(addrin);
2072 int i;
2073 int max;
2074 int sock;
2075 fd_set mset;
2076 fd_set rset;
2077
2078 /*
2079 * Set up the master fd_set. The set of descriptors we need
2080 * to select() for never changes anyway and it buys us a *lot*
2081 * of time to only build this once. However, if we ever choose
2082 * to not fork() for clients anymore, we may have to revisit
2083 * this.
2084 */
2085 max=0;
2086 FD_ZERO(&mset)do { int __d0, __d1; __asm__ __volatile__ ("cld; rep; " "stosq"
: "=c" (__d0), "=D" (__d1) : "a" (0), "0" (sizeof (fd_set) /
sizeof (__fd_mask)), "1" (&((&mset)->fds_bits)[0]
) : "memory"); } while (0)
;
2087 for(i=0;i<servers->len;i++) {
2088 if((sock=(g_array_index(servers, SERVER, i)(((SERVER*) (void *) (servers)->data) [(i)])).socket)) {
2089 FD_SET(sock, &mset)((void) (((&mset)->fds_bits)[((sock) / (8 * (int) sizeof
(__fd_mask)))] |= ((__fd_mask) 1 << ((sock) % (8 * (int
) sizeof (__fd_mask))))))
;
2090 max=sock>max?sock:max;
2091 }
2092 }
2093 if(modernsock) {
2094 FD_SET(modernsock, &mset)((void) (((&mset)->fds_bits)[((modernsock) / (8 * (int
) sizeof (__fd_mask)))] |= ((__fd_mask) 1 << ((modernsock
) % (8 * (int) sizeof (__fd_mask))))))
;
2095 max=modernsock>max?modernsock:max;
2096 }
2097 for(;;) {
2098 CLIENT *client = NULL((void*)0);
2099 pid_t *pid;
2100
2101 memcpy(&rset, &mset, sizeof(fd_set));
2102 if(select(max+1, &rset, NULL((void*)0), NULL((void*)0), NULL((void*)0))>0) {
2103 int net = 0;
2104 SERVER* serve=NULL((void*)0);
2105
2106 DEBUG("accept, ");
2107 if(FD_ISSET(modernsock, &rset)((((&rset)->fds_bits)[((modernsock) / (8 * (int) sizeof
(__fd_mask)))] & ((__fd_mask) 1 << ((modernsock) %
(8 * (int) sizeof (__fd_mask))))) != 0)
) {
2108 if((net=accept(modernsock, (struct sockaddr *) &addrin, &addrinlen)) < 0)
2109 err("accept: %m");
2110 client = negotiate(net, NULL((void*)0), servers, NEG_INIT(1 << 0) | NEG_MODERN(1 << 2));
2111 if(!client) {
2112 err_nonfatal("negotiation failed");
2113 close(net);
2114 net=0;
2115 continue;
2116 }
2117 serve = client->server;
2118 }
2119 for(i=0;i<servers->len && !net;i++) {
2120 serve=&(g_array_index(servers, SERVER, i)(((SERVER*) (void *) (servers)->data) [(i)]));
2121 if(FD_ISSET(serve->socket, &rset)((((&rset)->fds_bits)[((serve->socket) / (8 * (int)
sizeof (__fd_mask)))] & ((__fd_mask) 1 << ((serve->
socket) % (8 * (int) sizeof (__fd_mask))))) != 0)
) {
2122 if ((net=accept(serve->socket, (struct sockaddr *) &addrin, &addrinlen)) < 0)
2123 err("accept: %m");
2124 }
2125 }
2126 if(net) {
2127 int sock_flags;
2128
2129 if(serve->max_connections > 0 &&
2130 g_hash_table_size(children) >= serve->max_connections) {
2131 msg2(LOG_INFO, "Max connections reached")g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"Max connections reached"
)
;
2132 close(net);
2133 continue;
2134 }
2135 if((sock_flags = fcntl(net, F_GETFL3, 0))==-1) {
2136 err("fcntl F_GETFL");
2137 }
2138 if(fcntl(net, F_SETFL4, sock_flags &~O_NONBLOCK04000)==-1) {
2139 err("fcntl F_SETFL ~O_NONBLOCK");
2140 }
2141 if(!client) {
2142 client = g_new0(CLIENT, 1)((CLIENT *) g_malloc0_n ((1), sizeof (CLIENT)));
2143 client->server=serve;
2144 client->exportsize=OFFT_MAX~((off_t)1<<(sizeof(off_t)*8 -1));
2145 client->net=net;
2146 client->transactionlogfd = -1;
2147 }
2148 set_peername(net, client);
2149 if (!authorized_client(client)) {
2150 msg2(LOG_INFO,"Unauthorized client")g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"Unauthorized client"
)
;
2151 close(net);
2152 continue;
2153 }
2154 msg2(LOG_INFO,"Authorized client")g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"Authorized client"
)
;
2155 pid=g_malloc(sizeof(pid_t));
2156
2157 if (!dontfork) {
2158 if ((*pid=fork())<0) {
2159 msg3(LOG_INFO,"Could not fork (%s)",strerror(errno))g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"Could not fork (%s)"
,strerror((*__errno_location ())))
;
2160 close(net);
2161 continue;
2162 }
2163 if (*pid>0) { /* parent */
2164 close(net);
2165 g_hash_table_insert(children, pid, pid);
2166 continue;
2167 }
2168 /* child */
2169 g_hash_table_destroy(children);
2170 for(i=0;i<servers->len;i++) {
2171 serve=&g_array_index(servers, SERVER, i)(((SERVER*) (void *) (servers)->data) [(i)]);
2172 close(serve->socket);
2173 }
2174 /* FALSE does not free the
2175 actual data. This is required,
2176 because the client has a
2177 direct reference into that
2178 data, and otherwise we get a
2179 segfault... */
2180 g_array_free(servers, FALSE(0));
2181 }
2182
2183 msg2(LOG_INFO,"Starting to serve")g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, (char*)"Starting to serve"
)
;
2184 serveconnection(client);
2185 exit(EXIT_SUCCESS0);
2186 }
2187 }
2188 }
2189}
2190
2191void dosockopts(int socket) {
2192#ifndef sun
2193 int yes=1;
2194#else
2195 char yes='1';
2196#endif /* sun */
2197 struct linger l;
2198
2199 //int sock_flags;
2200
2201 /* lose the pesky "Address already in use" error message */
2202 if (setsockopt(socket,SOL_SOCKET1,SO_REUSEADDR2,&yes,sizeof(int)) == -1) {
2203 err("setsockopt SO_REUSEADDR");
2204 }
2205 l.l_onoff = 1;
2206 l.l_linger = 10;
2207 if (setsockopt(socket,SOL_SOCKET1,SO_LINGER13,&l,sizeof(l)) == -1) {
2208 perror("setsockopt SO_LINGER");
2209 exit(EXIT_FAILURE1);
2210 }
2211 if (setsockopt(socket,SOL_SOCKET1,SO_KEEPALIVE9,&yes,sizeof(int)) == -1) {
2212 err("setsockopt SO_KEEPALIVE");
2213 }
2214
2215 /* make the listening socket non-blocking */
2216 /*if ((sock_flags = fcntl(socket, F_GETFL, 0)) == -1) {
2217 err("fcntl F_GETFL");
2218 }
2219 if (fcntl(socket, F_SETFL, sock_flags | O_NONBLOCK) == -1) {
2220 err("fcntl F_SETFL O_NONBLOCK");
2221 }*/
2222}
2223
2224/**
2225 * Connect a server's socket.
2226 *
2227 * @param serve the server we want to connect.
2228 **/
2229int setup_serve(SERVER *serve) {
2230 struct addrinfo hints;
2231 struct addrinfo *ai = NULL((void*)0);
2232 gchar *port = NULL((void*)0);
2233 int e;
2234
2235 if(!do_oldstyle) {
2236 return serve->servename ? 1 : 0;
2237 }
2238 memset(&hints,'\0',sizeof(hints));
2239 hints.ai_flags = AI_PASSIVE0x0001 | AI_ADDRCONFIG0x0020 | AI_NUMERICSERV0x0400;
2240 hints.ai_socktype = SOCK_STREAMSOCK_STREAM;
2241 hints.ai_family = serve->socket_family;
2242
2243 port = g_strdup_printf ("%d", serve->port);
2244 if (port == NULL((void*)0))
2245 return 0;
2246
2247 e = getaddrinfo(serve->listenaddr,port,&hints,&ai);
2248
2249 g_free(port);
2250
2251 if(e != 0) {
2252 fprintf(stderrstderr, "getaddrinfo failed: %s\n", gai_strerror(e));
2253 serve->socket = -1;
2254 freeaddrinfo(ai);
2255 exit(EXIT_FAILURE1);
2256 }
2257
2258 if(serve->socket_family == AF_UNSPEC0)
2259 serve->socket_family = ai->ai_family;
2260
2261#ifdef WITH_SDP
2262 if ((serve->flags) && F_SDP32) {
2263 if (ai->ai_family == AF_INET2)
2264 ai->ai_family = AF_INET_SDP;
2265 else (ai->ai_family == AF_INET610)
2266 ai->ai_family = AF_INET6_SDP;
2267 }
2268#endif
2269 if ((serve->socket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0)
2270 err("socket: %m");
2271
2272 dosockopts(serve->socket);
2273
2274 DEBUG("Waiting for connections... bind, ");
2275 e = bind(serve->socket, ai->ai_addr, ai->ai_addrlen);
2276 if (e != 0 && errno(*__errno_location ()) != EADDRINUSE98)
2277 err("bind: %m");
2278 DEBUG("listen, ");
2279 if (listen(serve->socket, 1) < 0)
2280 err("listen: %m");
2281
2282 freeaddrinfo (ai);
2283 if(serve->servename) {
2284 return 1;
2285 } else {
2286 return 0;
2287 }
2288}
2289
2290void open_modern(void) {
2291 struct addrinfo hints;
2292 struct addrinfo* ai = NULL((void*)0);
2293 struct sock_flags;
2294 int e;
2295
2296 memset(&hints, '\0', sizeof(hints));
2297 hints.ai_flags = AI_PASSIVE0x0001 | AI_ADDRCONFIG0x0020;
2298 hints.ai_socktype = SOCK_STREAMSOCK_STREAM;
2299 hints.ai_family = AF_UNSPEC0;
2300 hints.ai_protocol = IPPROTO_TCPIPPROTO_TCP;
2301 e = getaddrinfo(modern_listen, modernport, &hints, &ai);
2302 if(e != 0) {
2303 fprintf(stderrstderr, "getaddrinfo failed: %s\n", gai_strerror(e));
2304 exit(EXIT_FAILURE1);
2305 }
2306 if((modernsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol))<0) {
2307 err("socket: %m");
2308 }
2309
2310 dosockopts(modernsock);
2311
2312 if(bind(modernsock, ai->ai_addr, ai->ai_addrlen)) {
2313 err("bind: %m");
2314 }
2315 if(listen(modernsock, 10) <0) {
2316 err("listen: %m");
2317 }
2318
2319 freeaddrinfo(ai);
2320}
2321
2322/**
2323 * Connect our servers.
2324 **/
2325void setup_servers(GArray* servers) {
2326 int i;
2327 struct sigaction sa;
2328 int want_modern=0;
2329
2330 for(i=0;i<servers->len;i++) {
2331 want_modern |= setup_serve(&(g_array_index(servers, SERVER, i)(((SERVER*) (void *) (servers)->data) [(i)])));
2332 }
2333 if(want_modern) {
2334 open_modern();
2335 }
2336 children=g_hash_table_new_full(g_int_hash, g_int_equal, NULL((void*)0), destroy_pid_t);
2337
2338 sa.sa_handler__sigaction_handler.sa_handler = sigchld_handler;
2339 sigemptyset(&sa.sa_mask);
2340 sa.sa_flags = SA_RESTART0x10000000;
2341 if(sigaction(SIGCHLD17, &sa, NULL((void*)0)) == -1)
2342 err("sigaction: %m");
2343 sa.sa_handler__sigaction_handler.sa_handler = sigterm_handler;
2344 sigemptyset(&sa.sa_mask);
2345 sa.sa_flags = SA_RESTART0x10000000;
2346 if(sigaction(SIGTERM15, &sa, NULL((void*)0)) == -1)
2347 err("sigaction: %m");
2348}
2349
2350/**
2351 * Go daemon (unless we specified at compile time that we didn't want this)
2352 * @param serve the first server of our configuration. If its port is zero,
2353 * then do not daemonize, because we're doing inetd then. This parameter
2354 * is only used to create a PID file of the form
2355 * /var/run/nbd-server.&lt;port&gt;.pid; it's not modified in any way.
2356 **/
2357#if !defined(NODAEMON)
2358void daemonize(SERVER* serve) {
2359 FILE*pidf;
2360
2361 if(serve && !(serve->port)) {
2362 return;
2363 }
2364 if(daemon(0,0)<0) {
2365 err("daemon");
2366 }
2367 if(!*pidftemplate) {
2368 if(serve) {
2369 strncpy(pidftemplate, "/var/run/nbd-server.%d.pid", 255);
2370 } else {
2371 strncpy(pidftemplate, "/var/run/nbd-server.pid", 255);
2372 }
2373 }
2374 snprintf(pidfname, 255, pidftemplate, serve ? serve->port : 0);
2375 pidf=fopen(pidfname, "w");
2376 if(pidf) {
2377 fprintf(pidf,"%d\n", (int)getpid());
2378 fclose(pidf);
2379 } else {
2380 perror("fopen");
2381 fprintf(stderrstderr, "Not fatal; continuing");
2382 }
2383}
2384#else
2385#define daemonize(serve)
2386#endif /* !defined(NODAEMON) */
2387
2388/*
2389 * Everything beyond this point (in the file) is run in non-daemon mode.
2390 * The stuff above daemonize() isn't.
2391 */
2392
2393void serve_err(SERVER* serve, const char* msg) G_GNUC_NORETURN__attribute__((__noreturn__));
2394
2395void serve_err(SERVER* serve, const char* msg) {
2396 g_message("Export of %s on port %d failed:", serve->exportname,g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, "Export of %s on port %d failed:"
, serve->exportname, serve->port)
2397 serve->port)g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, "Export of %s on port %d failed:"
, serve->exportname, serve->port)
;
2398 err(msg);
2399}
2400
2401/**
2402 * Set up user-ID and/or group-ID
2403 **/
2404void dousers(void) {
2405 struct passwd *pw;
2406 struct group *gr;
2407 gchar* str;
2408 if(rungroup) {
2409 gr=getgrnam(rungroup);
2410 if(!gr) {
2411 str = g_strdup_printf("Invalid group name: %s", rungroup);
2412 err(str);
2413 }
2414 if(setgid(gr->gr_gid)<0) {
2415 err("Could not set GID: %m");
2416 }
2417 }
2418 if(runuser) {
2419 pw=getpwnam(runuser);
2420 if(!pw) {
2421 str = g_strdup_printf("Invalid user name: %s", runuser);
2422 err(str);
2423 }
2424 if(setuid(pw->pw_uid)<0) {
2425 err("Could not set UID: %m");
2426 }
2427 }
2428}
2429
2430#ifndef ISSERVER
2431void glib_message_syslog_redirect(const gchar *log_domain,
2432 GLogLevelFlags log_level,
2433 const gchar *message,
2434 gpointer user_data)
2435{
2436 int level=LOG_DEBUG7;
2437
2438 switch( log_level )
2439 {
2440 case G_LOG_FLAG_FATAL:
2441 case G_LOG_LEVEL_CRITICAL:
2442 case G_LOG_LEVEL_ERROR:
2443 level=LOG_ERR3;
2444 break;
2445 case G_LOG_LEVEL_WARNING:
2446 level=LOG_WARNING4;
2447 break;
2448 case G_LOG_LEVEL_MESSAGE:
2449 case G_LOG_LEVEL_INFO:
2450 level=LOG_INFO6;
2451 break;
2452 case G_LOG_LEVEL_DEBUG:
2453 level=LOG_DEBUG7;
2454 break;
2455 default:
2456 level=LOG_ERR3;
2457 }
2458 syslog(level, "%s", message);
2459}
2460#endif
2461
2462/**
2463 * Main entry point...
2464 **/
2465int main(int argc, char *argv[]) {
2466 SERVER *serve;
2467 GArray *servers;
2468 GError *err=NULL((void*)0);
2469
2470 if (sizeof( struct nbd_request )!=28) {
2471 fprintf(stderrstderr,"Bad size of structure. Alignment problems?\n");
2472 exit(EXIT_FAILURE1) ;
2473 }
2474
2475 memset(pidftemplate, '\0', 256);
2476
2477 logging();
2478 config_file_pos = g_strdup(CFILE"/usr/local/etc" "/nbd-server/config");
2479 serve=cmdline(argc, argv);
2480 servers = parse_cfile(config_file_pos, TRUE(!(0)), &err);
2481
2482 if(serve) {
2483 serve->socket_family = AF_UNSPEC0;
2484
2485 append_serve(serve, servers);
2486
2487 if (!(serve->port)) {
2488 CLIENT *client;
2489#ifndef ISSERVER
2490 /* You really should define ISSERVER if you're going to use
2491 * inetd mode, but if you don't, closing stdout and stderr
2492 * (which inetd had connected to the client socket) will let it
2493 * work. */
2494 close(1);
2495 close(2);
2496 open("/dev/null", O_WRONLY01);
2497 open("/dev/null", O_WRONLY01);
2498 g_log_set_default_handler( glib_message_syslog_redirect, NULL((void*)0) );
2499#endif
2500 client=g_malloc(sizeof(CLIENT));
2501 client->server=serve;
2502 client->net=0;
2503 client->exportsize=OFFT_MAX~((off_t)1<<(sizeof(off_t)*8 -1));
2504 set_peername(0,client);
2505 serveconnection(client);
2506 return 0;
2507 }
2508 }
2509
2510 if(!servers || !servers->len) {
2511 if(err && !(err->domain == g_quark_from_string("parse_cfile")
2512 && err->code == CFILE_NOTFOUND)) {
2513 g_warning("Could not parse config file: %s",g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "Could not parse config file: %s"
, err ? err->message : "Unknown error")
2514 err ? err->message : "Unknown error")g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "Could not parse config file: %s"
, err ? err->message : "Unknown error")
;
2515 }
2516 }
2517 if(serve) {
2518 g_warning("Specifying an export on the command line is deprecated.")g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "Specifying an export on the command line is deprecated."
)
;
2519 g_warning("Please use a configuration file instead.")g_log (((gchar*) 0), G_LOG_LEVEL_WARNING, "Please use a configuration file instead."
)
;
2520 }
2521
2522 if((!serve) && (!servers||!servers->len)) {
2523 g_message("No configured exports; quitting.")g_log (((gchar*) 0), G_LOG_LEVEL_MESSAGE, "No configured exports; quitting."
)
;
2524 exit(EXIT_FAILURE1);
2525 }
2526 if (!dontfork)
2527 daemonize(serve);
2528 setup_servers(servers);
2529 dousers();
2530 serveloop(servers);
2531 return 0 ;
2532}