00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056 #include "lfs.h"
00057
00058 #include <sys/types.h>
00059 #include <sys/socket.h>
00060 #include <sys/stat.h>
00061 #include <sys/select.h>
00062 #include <sys/wait.h>
00063 #ifdef HAVE_SYS_IOCTL_H
00064 #include <sys/ioctl.h>
00065 #endif
00066 #include <sys/param.h>
00067 #ifdef HAVE_SYS_MOUNT_H
00068 #include <sys/mount.h>
00069 #endif
00070 #include <signal.h>
00071 #include <netinet/tcp.h>
00072 #include <netinet/in.h>
00073 #include <netdb.h>
00074 #include <syslog.h>
00075 #include <unistd.h>
00076 #include <stdio.h>
00077 #include <stdlib.h>
00078 #include <string.h>
00079 #include <fcntl.h>
00080 #include <arpa/inet.h>
00081 #include <strings.h>
00082 #include <dirent.h>
00083 #include <unistd.h>
00084 #include <getopt.h>
00085 #include <pwd.h>
00086 #include <grp.h>
00087
00088 #include <glib.h>
00089
00090
00091 #define MY_NAME "nbd_server"
00092 #include "cliserv.h"
00093
00094
00095 #ifndef SYSCONFDIR
00096 #define SYSCONFDIR "/etc"
00097 #endif
00098 #define CFILE SYSCONFDIR "/nbd-server/config"
00099
00100
00101 gchar* config_file_pos;
00102
00103
00104 gchar* runuser=NULL;
00105
00106 gchar* rungroup=NULL;
00107
00108
00109 #ifdef ISSERVER
00110 #define msg2(a,b) syslog(a,b)
00111 #define msg3(a,b,c) syslog(a,b,c)
00112 #define msg4(a,b,c,d) syslog(a,b,c,d)
00113 #else
00114 #define msg2(a,b) g_message(b)
00115 #define msg3(a,b,c) g_message(b,c)
00116 #define msg4(a,b,c,d) g_message(b,c,d)
00117 #endif
00118
00119
00120
00121 #ifdef DODBG
00122 #define DEBUG( a ) printf( a )
00123 #define DEBUG2( a,b ) printf( a,b )
00124 #define DEBUG3( a,b,c ) printf( a,b,c )
00125 #define DEBUG4( a,b,c,d ) printf( a,b,c,d )
00126 #else
00127 #define DEBUG( a )
00128 #define DEBUG2( a,b )
00129 #define DEBUG3( a,b,c )
00130 #define DEBUG4( a,b,c,d )
00131 #endif
00132 #ifndef PACKAGE_VERSION
00133 #define PACKAGE_VERSION ""
00134 #endif
00135
00136
00137
00138
00139 #define OFFT_MAX ~((off_t)1<<(sizeof(off_t)*8-1))
00140 #define LINELEN 256
00141
00142 #define BUFSIZE (1024*1024)
00143 #define DIFFPAGESIZE 4096
00144 #define F_READONLY 1
00145 #define F_MULTIFILE 2
00146 #define F_COPYONWRITE 4
00147
00148 #define F_AUTOREADONLY 8
00149 #define F_SPARSE 16
00150 GHashTable *children;
00151 char pidfname[256];
00152 char pidftemplate[256];
00153 char default_authname[] = SYSCONFDIR "/nbd-server/allow";
00154
00155
00156
00157
00158 typedef enum {
00159 VIRT_NONE=0,
00160 VIRT_IPLIT,
00161 VIRT_IPHASH,
00162
00163 VIRT_CIDR,
00164 } VIRT_STYLE;
00165
00166
00167
00168
00169 typedef struct {
00170 gchar* exportname;
00171 off_t expected_size;
00172
00173 unsigned int port;
00174 char* authname;
00175 int flags;
00176 unsigned int timeout;
00177
00178 int socket;
00179 VIRT_STYLE virtstyle;
00180 uint8_t cidrlen;
00181
00182 } SERVER;
00183
00184
00185
00186
00187 typedef struct {
00188 int fhandle;
00189 off_t startoff;
00190 } FILE_INFO;
00191
00192 typedef struct {
00193 off_t exportsize;
00194 char *clientname;
00195 char *exportname;
00196 GArray *export;
00197
00198
00199 int net;
00200 SERVER *server;
00201 char* difffilename;
00202 int difffile;
00203
00204
00205 u32 difffilelen;
00206 u32 *difmap;
00207 } CLIENT;
00208
00209
00210
00211
00212 typedef enum {
00213 PARAM_INT,
00214 PARAM_STRING,
00215 PARAM_BOOL,
00216 } PARAM_TYPE;
00217
00218
00219
00220
00221 typedef struct {
00222 gchar *paramname;
00223
00224 gboolean required;
00225
00226 PARAM_TYPE ptype;
00227 gpointer target;
00228
00229
00230
00231 gint flagval;
00232
00233 } PARAM;
00234
00235
00236
00237
00238
00239
00240
00241
00242 int authorized_client(CLIENT *opts) {
00243 const char *ERRMSG="Invalid entry '%s' in authfile '%s', so, refusing all connections.";
00244 FILE *f ;
00245 char line[LINELEN];
00246 char *tmp;
00247 struct in_addr addr;
00248 struct in_addr client;
00249 struct in_addr cltemp;
00250 int len;
00251
00252 if ((f=fopen(opts->server->authname,"r"))==NULL) {
00253 msg4(LOG_INFO,"Can't open authorization file %s (%s).",
00254 opts->server->authname,strerror(errno)) ;
00255 return 1 ;
00256 }
00257
00258 inet_aton(opts->clientname, &client);
00259 while (fgets(line,LINELEN,f)!=NULL) {
00260 if((tmp=index(line, '/'))) {
00261 if(strlen(line)<=tmp-line) {
00262 msg4(LOG_CRIT, ERRMSG, line, opts->server->authname);
00263 return 0;
00264 }
00265 *(tmp++)=0;
00266 if(inet_aton(line,&addr)) {
00267 msg4(LOG_CRIT, ERRMSG, line, opts->server->authname);
00268 return 0;
00269 }
00270 len=strtol(tmp, NULL, 0);
00271 addr.s_addr>>=32-len;
00272 addr.s_addr<<=32-len;
00273 memcpy(&cltemp,&client,sizeof(client));
00274 cltemp.s_addr>>=32-len;
00275 cltemp.s_addr<<=32-len;
00276 if(addr.s_addr == cltemp.s_addr) {
00277 return 1;
00278 }
00279 }
00280 if (strncmp(line,opts->clientname,strlen(opts->clientname))==0) {
00281 fclose(f);
00282 return 1;
00283 }
00284 }
00285 fclose(f);
00286 return 0;
00287 }
00288
00289
00290
00291
00292
00293
00294
00295
00296 inline void readit(int f, void *buf, size_t len) {
00297 ssize_t res;
00298 while (len > 0) {
00299 DEBUG("*");
00300 if ((res = read(f, buf, len)) <= 0)
00301 err("Read failed: %m");
00302 len -= res;
00303 buf += res;
00304 }
00305 }
00306
00307
00308
00309
00310
00311
00312
00313
00314 inline void writeit(int f, void *buf, size_t len) {
00315 ssize_t res;
00316 while (len > 0) {
00317 DEBUG("+");
00318 if ((res = write(f, buf, len)) <= 0)
00319 err("Send failed: %m");
00320 len -= res;
00321 buf += res;
00322 }
00323 }
00324
00325
00326
00327
00328
00329 void usage() {
00330 printf("This is nbd-server version " VERSION "\n");
00331 printf("Usage: port file_to_export [size][kKmM] [-l authorize_file] [-r] [-m] [-c] [-a timeout_sec] [-C configuration file] [-p PID file name] [-o section name]\n"
00332 "\t-r|--read-only\t\tread only\n"
00333 "\t-m|--multi-file\t\tmultiple file\n"
00334 "\t-c|--copy-on-write\tcopy on write\n"
00335 "\t-C|--config-file\tspecify an alternate configuration file\n"
00336 "\t-l|--authorize-file\tfile with list of hosts that are allowed to\n\t\t\t\tconnect.\n"
00337 "\t-a|--idle-time\t\tmaximum idle seconds; server terminates when\n\t\t\t\tidle time exceeded\n"
00338 "\t-p|--pid-file\t\tspecify a filename to write our PID to\n"
00339 "\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\n"
00340 "\tif port is set to 0, stdin is used (for running from inetd)\n"
00341 "\tif file_to_export contains '%%s', it is substituted with the IP\n"
00342 "\t\taddress of the machine trying to connect\n" );
00343 printf("Using configuration file %s\n", CFILE);
00344 }
00345
00346
00347 void dump_section(SERVER* serve, gchar* section_header) {
00348 printf("[%s]\n", section_header);
00349 printf("\texportname = %s\n", serve->exportname);
00350 printf("\tport = %d\n", serve->port);
00351 if(serve->flags & F_READONLY) {
00352 printf("\treadonly = true\n");
00353 }
00354 if(serve->flags & F_MULTIFILE) {
00355 printf("\tmultifile = true\n");
00356 }
00357 if(serve->flags & F_COPYONWRITE) {
00358 printf("\tcopyonwrite = true\n");
00359 }
00360 if(serve->expected_size) {
00361 printf("\tfilesize = %Ld\n", (long long int)serve->expected_size);
00362 }
00363 if(serve->authname) {
00364 printf("\tauthfile = %s\n", serve->authname);
00365 }
00366 if(serve->timeout) {
00367 printf("\ttimeout = %d\n", serve->timeout);
00368 }
00369 exit(EXIT_SUCCESS);
00370 }
00371
00372
00373
00374
00375
00376
00377
00378 SERVER* cmdline(int argc, char *argv[]) {
00379 int i=0;
00380 int nonspecial=0;
00381 int c;
00382 struct option long_options[] = {
00383 {"read-only", no_argument, NULL, 'r'},
00384 {"multi-file", no_argument, NULL, 'm'},
00385 {"copy-on-write", no_argument, NULL, 'c'},
00386 {"authorize-file", required_argument, NULL, 'l'},
00387 {"idle-time", required_argument, NULL, 'a'},
00388 {"config-file", required_argument, NULL, 'C'},
00389 {"pid-file", required_argument, NULL, 'p'},
00390 {"output-config", required_argument, NULL, 'o'},
00391 {0,0,0,0}
00392 };
00393 SERVER *serve;
00394 off_t es;
00395 size_t last;
00396 char suffix;
00397 gboolean do_output=FALSE;
00398 gchar* section_header=NULL;
00399
00400 if(argc==1) {
00401 return NULL;
00402 }
00403 serve=g_new0(SERVER, 1);
00404 serve->authname = g_strdup(default_authname);
00405 while((c=getopt_long(argc, argv, "-a:C:cl:mo:rp:", long_options, &i))>=0) {
00406 switch (c) {
00407 case 1:
00408
00409 switch(nonspecial++) {
00410 case 0:
00411 serve->port=strtol(optarg, NULL, 0);
00412 break;
00413 case 1:
00414 serve->exportname = g_strdup(optarg);
00415 if(serve->exportname[0] != '/') {
00416 fprintf(stderr, "E: The to be exported file needs to be an absolute filename!\n");
00417 exit(EXIT_FAILURE);
00418 }
00419 break;
00420 case 2:
00421 last=strlen(optarg)-1;
00422 suffix=optarg[last];
00423 if (suffix == 'k' || suffix == 'K' ||
00424 suffix == 'm' || suffix == 'M')
00425 optarg[last] = '\0';
00426 es = (off_t)atol(optarg);
00427 switch (suffix) {
00428 case 'm':
00429 case 'M': es <<= 10;
00430 case 'k':
00431 case 'K': es <<= 10;
00432 default : break;
00433 }
00434 serve->expected_size = es;
00435 break;
00436 }
00437 break;
00438 case 'r':
00439 serve->flags |= F_READONLY;
00440 break;
00441 case 'm':
00442 serve->flags |= F_MULTIFILE;
00443 break;
00444 case 'o':
00445 do_output = TRUE;
00446 section_header = g_strdup(optarg);
00447 break;
00448 case 'p':
00449 strncpy(pidftemplate, optarg, 256);
00450 break;
00451 case 'c':
00452 serve->flags |=F_COPYONWRITE;
00453 break;
00454 case 'C':
00455 g_free(config_file_pos);
00456 config_file_pos=g_strdup(optarg);
00457 break;
00458 case 'l':
00459 g_free(serve->authname);
00460 serve->authname=g_strdup(optarg);
00461 break;
00462 case 'a':
00463 serve->timeout=strtol(optarg, NULL, 0);
00464 break;
00465 default:
00466 usage();
00467 exit(EXIT_FAILURE);
00468 break;
00469 }
00470 }
00471
00472
00473 if(nonspecial<2) {
00474 g_free(serve);
00475 serve=NULL;
00476 }
00477 if(do_output) {
00478 if(!serve) {
00479 g_critical("Need a complete configuration on the command line to output a config file section!");
00480 exit(EXIT_FAILURE);
00481 }
00482 dump_section(serve, section_header);
00483 }
00484 return serve;
00485 }
00486
00487
00488
00489
00490 typedef enum {
00491 CFILE_NOTFOUND,
00492 CFILE_MISSING_GENERIC,
00493 CFILE_KEY_MISSING,
00494 CFILE_VALUE_INVALID,
00495 CFILE_PROGERR
00496 } CFILE_ERRORS;
00497
00498
00499
00500
00501 void remove_server(gpointer s) {
00502 SERVER *server;
00503
00504 server=(SERVER*)s;
00505 g_free(server->exportname);
00506 if(server->authname)
00507 g_free(server->authname);
00508 g_free(server);
00509 }
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521 GArray* parse_cfile(gchar* f, GError** e) {
00522 const char* DEFAULT_ERROR = "Could not parse %s in group %s: %s";
00523 const char* MISSING_REQUIRED_ERROR = "Could not find required value %s in group %s: %s";
00524 SERVER s;
00525 gchar *virtstyle=NULL;
00526 PARAM lp[] = {
00527 { "exportname", TRUE, PARAM_STRING, NULL, 0 },
00528 { "port", TRUE, PARAM_INT, NULL, 0 },
00529 { "authfile", FALSE, PARAM_STRING, NULL, 0 },
00530 { "timeout", FALSE, PARAM_INT, NULL, 0 },
00531 { "filesize", FALSE, PARAM_INT, NULL, 0 },
00532 { "virtstyle", FALSE, PARAM_STRING, NULL, 0 },
00533 { "readonly", FALSE, PARAM_BOOL, NULL, F_READONLY },
00534 { "multifile", FALSE, PARAM_BOOL, NULL, F_MULTIFILE },
00535 { "copyonwrite", FALSE, PARAM_BOOL, NULL, F_COPYONWRITE },
00536 { "autoreadonly", FALSE, PARAM_BOOL, NULL, F_AUTOREADONLY },
00537 { "sparse_cow", FALSE, PARAM_BOOL, NULL, F_SPARSE },
00538 };
00539 const int lp_size=11;
00540 PARAM gp[] = {
00541 { "user", FALSE, PARAM_STRING, &runuser, 0 },
00542 { "group", FALSE, PARAM_STRING, &rungroup, 0 },
00543 };
00544 PARAM* p=gp;
00545 int p_size=2;
00546 GKeyFile *cfile;
00547 GError *err = NULL;
00548 const char *err_msg=NULL;
00549 GQuark errdomain;
00550 GArray *retval=NULL;
00551 gchar **groups;
00552 gboolean value;
00553 gint i;
00554 gint j;
00555
00556 errdomain = g_quark_from_string("parse_cfile");
00557 cfile = g_key_file_new();
00558 retval = g_array_new(FALSE, TRUE, sizeof(SERVER));
00559 if(!g_key_file_load_from_file(cfile, f, G_KEY_FILE_KEEP_COMMENTS |
00560 G_KEY_FILE_KEEP_TRANSLATIONS, &err)) {
00561 g_set_error(e, errdomain, CFILE_NOTFOUND, "Could not open config file.");
00562 g_key_file_free(cfile);
00563 return retval;
00564 }
00565 if(strcmp(g_key_file_get_start_group(cfile), "generic")) {
00566 g_set_error(e, errdomain, CFILE_MISSING_GENERIC, "Config file does not contain the [generic] group!");
00567 g_key_file_free(cfile);
00568 return NULL;
00569 }
00570 groups = g_key_file_get_groups(cfile, NULL);
00571 for(i=0;groups[i];i++) {
00572 memset(&s, '\0', sizeof(SERVER));
00573 lp[0].target=&(s.exportname);
00574 lp[1].target=&(s.port);
00575 lp[2].target=&(s.authname);
00576 lp[3].target=&(s.timeout);
00577 lp[4].target=&(s.expected_size);
00578 lp[5].target=&(virtstyle);
00579 lp[6].target=lp[7].target=lp[8].target=
00580 lp[9].target=lp[10].target=&(s.flags);
00581
00582 if(i==1) {
00583 p=lp;
00584 p_size=lp_size;
00585 }
00586 for(j=0;j<p_size;j++) {
00587 g_assert(p[j].target != NULL);
00588 g_assert(p[j].ptype==PARAM_INT||p[j].ptype==PARAM_STRING||p[j].ptype==PARAM_BOOL);
00589 switch(p[j].ptype) {
00590 case PARAM_INT:
00591 *((gint*)p[j].target) =
00592 g_key_file_get_integer(cfile,
00593 groups[i],
00594 p[j].paramname,
00595 &err);
00596 break;
00597 case PARAM_STRING:
00598 *((gchar**)p[j].target) =
00599 g_key_file_get_string(cfile,
00600 groups[i],
00601 p[j].paramname,
00602 &err);
00603 break;
00604 case PARAM_BOOL:
00605 value = g_key_file_get_boolean(cfile,
00606 groups[i],
00607 p[j].paramname, &err);
00608 if(!err) {
00609 if(value) {
00610 *((gint*)p[j].target) |= p[j].flagval;
00611 } else {
00612 *((gint*)p[j].target) &= ~(p[j].flagval);
00613 }
00614 }
00615 break;
00616 }
00617 if(err) {
00618 if(err->code == G_KEY_FILE_ERROR_KEY_NOT_FOUND) {
00619 if(!p[j].required) {
00620
00621 g_clear_error(&err);
00622 continue;
00623 } else {
00624 err_msg = MISSING_REQUIRED_ERROR;
00625 }
00626 } else {
00627 err_msg = DEFAULT_ERROR;
00628 }
00629 g_set_error(e, errdomain, CFILE_VALUE_INVALID, err_msg, p[j].paramname, groups[i], err->message);
00630 g_array_free(retval, TRUE);
00631 g_error_free(err);
00632 g_key_file_free(cfile);
00633 return NULL;
00634 }
00635 }
00636 if(virtstyle) {
00637 if(!strncmp(virtstyle, "none", 4)) {
00638 s.virtstyle=VIRT_NONE;
00639 } else if(!strncmp(virtstyle, "ipliteral", 9)) {
00640 s.virtstyle=VIRT_IPLIT;
00641 } else if(!strncmp(virtstyle, "iphash", 6)) {
00642 s.virtstyle=VIRT_IPHASH;
00643 } else if(!strncmp(virtstyle, "cidrhash", 8)) {
00644 s.virtstyle=VIRT_CIDR;
00645 if(strlen(virtstyle)<10) {
00646 g_set_error(e, errdomain, CFILE_VALUE_INVALID, "Invalid value %s for parameter virtstyle in group %s: missing length", virtstyle, groups[i]);
00647 g_array_free(retval, TRUE);
00648 g_key_file_free(cfile);
00649 return NULL;
00650 }
00651 s.cidrlen=strtol(virtstyle+8, NULL, 0);
00652 } else {
00653 g_set_error(e, errdomain, CFILE_VALUE_INVALID, "Invalid value %s for parameter virtstyle in group %s", virtstyle, groups[i]);
00654 g_array_free(retval, TRUE);
00655 g_key_file_free(cfile);
00656 return NULL;
00657 }
00658 } else {
00659 s.virtstyle=VIRT_IPLIT;
00660 }
00661
00662 virtstyle=NULL;
00663
00664 if(i>0) {
00665 g_array_append_val(retval, s);
00666 }
00667 }
00668 return retval;
00669 }
00670
00671
00672
00673
00674
00675
00676 void sigchld_handler(int s) {
00677 int status;
00678 int* i;
00679 pid_t pid;
00680
00681 while((pid=waitpid(-1, &status, WNOHANG)) > 0) {
00682 if(WIFEXITED(status)) {
00683 msg3(LOG_INFO, "Child exited with %d", WEXITSTATUS(status));
00684 }
00685 i=g_hash_table_lookup(children, &pid);
00686 if(!i) {
00687 msg3(LOG_INFO, "SIGCHLD received for an unknown child with PID %ld", (long)pid);
00688 } else {
00689 DEBUG2("Removing %d from the list of children", pid);
00690 g_hash_table_remove(children, &pid);
00691 }
00692 }
00693 }
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703 void killchild(gpointer key, gpointer value, gpointer user_data) {
00704 pid_t *pid=value;
00705 int *parent=user_data;
00706
00707 kill(*pid, SIGTERM);
00708 *parent=1;
00709 }
00710
00711
00712
00713
00714
00715
00716 void sigterm_handler(int s) {
00717 int parent=0;
00718
00719 g_hash_table_foreach(children, killchild, &parent);
00720
00721 if(parent) {
00722 unlink(pidfname);
00723 }
00724
00725 exit(0);
00726 }
00727
00728
00729
00730
00731
00732
00733
00734
00735 off_t size_autodetect(int fhandle) {
00736 off_t es;
00737 unsigned long sectors;
00738 struct stat stat_buf;
00739 int error;
00740
00741 #ifdef HAVE_SYS_MOUNT_H
00742 #ifdef HAVE_SYS_IOCTL_H
00743 #ifdef BLKGETSIZE
00744 DEBUG("looking for export size with ioctl BLKGETSIZE\n");
00745 if (!ioctl(fhandle, BLKGETSIZE, §ors) && sectors) {
00746 es = (off_t)sectors * (off_t)512;
00747 return es;
00748 }
00749 #endif
00750 #endif
00751 #endif
00752
00753 DEBUG("looking for fhandle size with fstat\n");
00754 stat_buf.st_size = 0;
00755 error = fstat(fhandle, &stat_buf);
00756 if (!error) {
00757 if(stat_buf.st_size > 0)
00758 return (off_t)stat_buf.st_size;
00759 } else {
00760 err("fstat failed: %m");
00761 }
00762
00763 DEBUG("looking for fhandle size with lseek SEEK_END\n");
00764 es = lseek(fhandle, (off_t)0, SEEK_END);
00765 if (es > ((off_t)0)) {
00766 return es;
00767 } else {
00768 DEBUG2("lseek failed: %d", errno==EBADF?1:(errno==ESPIPE?2:(errno==EINVAL?3:4)));
00769 }
00770
00771 err("Could not find size of exported block device: %m");
00772 return OFFT_MAX;
00773 }
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786 int get_filepos(GArray* export, off_t a, int* fhandle, off_t* foffset, size_t* maxbytes ) {
00787
00788 if(a < 0)
00789 return -1;
00790
00791
00792 FILE_INFO fi;
00793 int start = 0;
00794 int end = export->len - 1;
00795 while( start <= end ) {
00796 int mid = (start + end) / 2;
00797 fi = g_array_index(export, FILE_INFO, mid);
00798 if( fi.startoff < a ) {
00799 start = mid + 1;
00800 } else if( fi.startoff > a ) {
00801 end = mid - 1;
00802 } else {
00803 start = end = mid;
00804 break;
00805 }
00806 }
00807
00808
00809 g_assert(end >= 0);
00810
00811 fi = g_array_index(export, FILE_INFO, end);
00812 *fhandle = fi.fhandle;
00813 *foffset = a - fi.startoff;
00814 *maxbytes = 0;
00815 if( end+1 < export->len ) {
00816 FILE_INFO fi_next = g_array_index(export, FILE_INFO, end+1);
00817 *maxbytes = fi_next.startoff - a;
00818 }
00819
00820 return 0;
00821 }
00822
00823
00824
00825
00826
00827
00828
00829
00830
00831 void myseek(int handle,off_t a) {
00832 if (lseek(handle, a, SEEK_SET) < 0) {
00833 err("Can not seek locally!\n");
00834 }
00835 }
00836
00837
00838
00839
00840
00841
00842
00843
00844
00845
00846
00847 ssize_t rawexpwrite(off_t a, char *buf, size_t len, CLIENT *client) {
00848 int fhandle;
00849 off_t foffset;
00850 size_t maxbytes;
00851
00852 if(get_filepos(client->export, a, &fhandle, &foffset, &maxbytes))
00853 return -1;
00854 if(maxbytes && len > maxbytes)
00855 len = maxbytes;
00856
00857 DEBUG4("(WRITE to fd %d offset %Lu len %u), ", fhandle, foffset, len);
00858
00859 myseek(fhandle, foffset);
00860 return write(fhandle, buf, len);
00861 }
00862
00863
00864
00865
00866
00867 int rawexpwrite_fully(off_t a, char *buf, size_t len, CLIENT *client) {
00868 ssize_t ret = 0;
00869
00870 while(len > 0 && (ret=rawexpwrite(a, buf, len, client)) > 0 ) {
00871 a += ret;
00872 buf += ret;
00873 len -= ret;
00874 }
00875 return (ret < 0 || len != 0);
00876 }
00877
00878
00879
00880
00881
00882
00883
00884
00885
00886
00887
00888
00889 ssize_t rawexpread(off_t a, char *buf, size_t len, CLIENT *client) {
00890 int fhandle;
00891 off_t foffset;
00892 size_t maxbytes;
00893
00894 if(get_filepos(client->export, a, &fhandle, &foffset, &maxbytes))
00895 return -1;
00896 if(maxbytes && len > maxbytes)
00897 len = maxbytes;
00898
00899 DEBUG4("(READ from fd %d offset %Lu len %u), ", fhandle, foffset, len);
00900
00901 myseek(fhandle, foffset);
00902 return read(fhandle, buf, len);
00903 }
00904
00905
00906
00907
00908
00909 int rawexpread_fully(off_t a, char *buf, size_t len, CLIENT *client) {
00910 ssize_t ret = 0;
00911
00912 while(len > 0 && (ret=rawexpread(a, buf, len, client)) > 0 ) {
00913 a += ret;
00914 buf += ret;
00915 len -= ret;
00916 }
00917 return (ret < 0 || len != 0);
00918 }
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929
00930 int expread(off_t a, char *buf, size_t len, CLIENT *client) {
00931 off_t rdlen, offset;
00932 off_t mapcnt, mapl, maph, pagestart;
00933
00934 if (!(client->server->flags & F_COPYONWRITE))
00935 return(rawexpread_fully(a, buf, len, client));
00936 DEBUG3("Asked to read %d bytes at %Lu.\n", len, (unsigned long long)a);
00937
00938 mapl=a/DIFFPAGESIZE; maph=(a+len-1)/DIFFPAGESIZE;
00939
00940 for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
00941 pagestart=mapcnt*DIFFPAGESIZE;
00942 offset=a-pagestart;
00943 rdlen=(0<DIFFPAGESIZE-offset && len<(size_t)(DIFFPAGESIZE-offset)) ?
00944 len : (size_t)DIFFPAGESIZE-offset;
00945 if (client->difmap[mapcnt]!=(u32)(-1)) {
00946 DEBUG3("Page %Lu is at %lu\n", (unsigned long long)mapcnt,
00947 (unsigned long)(client->difmap[mapcnt]));
00948 myseek(client->difffile, client->difmap[mapcnt]*DIFFPAGESIZE+offset);
00949 if (read(client->difffile, buf, rdlen) != rdlen) return -1;
00950 } else {
00951 DEBUG2("Page %Lu is not here, we read the original one\n",
00952 (unsigned long long)mapcnt);
00953 if(rawexpread_fully(a, buf, rdlen, client)) return -1;
00954 }
00955 len-=rdlen; a+=rdlen; buf+=rdlen;
00956 }
00957 return 0;
00958 }
00959
00960
00961
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971 int expwrite(off_t a, char *buf, size_t len, CLIENT *client) {
00972 char pagebuf[DIFFPAGESIZE];
00973 off_t mapcnt,mapl,maph;
00974 off_t wrlen,rdlen;
00975 off_t pagestart;
00976 off_t offset;
00977
00978 if (!(client->server->flags & F_COPYONWRITE))
00979 return(rawexpwrite_fully(a, buf, len, client));
00980 DEBUG3("Asked to write %d bytes at %Lu.\n", len, (unsigned long long)a);
00981
00982 mapl=a/DIFFPAGESIZE ; maph=(a+len-1)/DIFFPAGESIZE ;
00983
00984 for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
00985 pagestart=mapcnt*DIFFPAGESIZE ;
00986 offset=a-pagestart ;
00987 wrlen=(0<DIFFPAGESIZE-offset && len<(size_t)(DIFFPAGESIZE-offset)) ?
00988 len : (size_t)DIFFPAGESIZE-offset;
00989
00990 if (client->difmap[mapcnt]!=(u32)(-1)) {
00991 DEBUG3("Page %Lu is at %lu\n", (unsigned long long)mapcnt,
00992 (unsigned long)(client->difmap[mapcnt])) ;
00993 myseek(client->difffile,
00994 client->difmap[mapcnt]*DIFFPAGESIZE+offset);
00995 if (write(client->difffile, buf, wrlen) != wrlen) return -1 ;
00996 } else {
00997 myseek(client->difffile,client->difffilelen*DIFFPAGESIZE) ;
00998 client->difmap[mapcnt]=(client->server->flags&F_SPARSE)?mapcnt:client->difffilelen++;
00999 DEBUG3("Page %Lu is not here, we put it at %lu\n",
01000 (unsigned long long)mapcnt,
01001 (unsigned long)(client->difmap[mapcnt]));
01002 rdlen=DIFFPAGESIZE ;
01003 if (rawexpread_fully(pagestart, pagebuf, rdlen, client))
01004 return -1;
01005 memcpy(pagebuf+offset,buf,wrlen) ;
01006 if (write(client->difffile, pagebuf, DIFFPAGESIZE) !=
01007 DIFFPAGESIZE)
01008 return -1;
01009 }
01010 len-=wrlen ; a+=wrlen ; buf+=wrlen ;
01011 }
01012 return 0;
01013 }
01014
01015
01016
01017
01018
01019
01020 void negotiate(CLIENT *client) {
01021 char zeros[300];
01022 u64 size_host;
01023
01024 memset(zeros, '\0', 290);
01025 if (write(client->net, INIT_PASSWD, 8) < 0)
01026 err("Negotiation failed: %m");
01027 cliserv_magic = htonll(cliserv_magic);
01028 if (write(client->net, &cliserv_magic, sizeof(cliserv_magic)) < 0)
01029 err("Negotiation failed: %m");
01030 size_host = htonll((u64)(client->exportsize));
01031 if (write(client->net, &size_host, 8) < 0)
01032 err("Negotiation failed: %m");
01033 if (write(client->net, zeros, 128) < 0)
01034 err("Negotiation failed: %m");
01035 }
01036
01037
01038 #define SEND(net,reply) writeit( net, &reply, sizeof( reply ));
01039
01040 #define ERROR(client,reply) { reply.error = htonl(-1); SEND(client->net,reply); reply.error = 0; }
01041
01042
01043
01044
01045
01046
01047
01048
01049
01050 int mainloop(CLIENT *client) {
01051 struct nbd_request request;
01052 struct nbd_reply reply;
01053 gboolean go_on=TRUE;
01054 #ifdef DODBG
01055 int i = 0;
01056 #endif
01057 negotiate(client);
01058 DEBUG("Entering request loop!\n");
01059 reply.magic = htonl(NBD_REPLY_MAGIC);
01060 reply.error = 0;
01061 while (go_on) {
01062 char buf[BUFSIZE];
01063 size_t len;
01064 #ifdef DODBG
01065 i++;
01066 printf("%d: ", i);
01067 #endif
01068 if (client->server->timeout)
01069 alarm(client->server->timeout);
01070 readit(client->net, &request, sizeof(request));
01071 request.from = ntohll(request.from);
01072 request.type = ntohl(request.type);
01073
01074 if (request.type==NBD_CMD_DISC) {
01075 msg2(LOG_INFO, "Disconnect request received.");
01076 if (client->server->flags & F_COPYONWRITE) {
01077 if (client->difmap) g_free(client->difmap) ;
01078 close(client->difffile);
01079 unlink(client->difffilename);
01080 free(client->difffilename);
01081 }
01082 go_on=FALSE;
01083 continue;
01084 }
01085
01086 len = ntohl(request.len);
01087
01088 if (request.magic != htonl(NBD_REQUEST_MAGIC))
01089 err("Not enough magic.");
01090 if (len > BUFSIZE + sizeof(struct nbd_reply))
01091 err("Request too big!");
01092 #ifdef DODBG
01093 printf("%s from %Lu (%Lu) len %d, ", request.type ? "WRITE" :
01094 "READ", (unsigned long long)request.from,
01095 (unsigned long long)request.from / 512, len);
01096 #endif
01097 memcpy(reply.handle, request.handle, sizeof(reply.handle));
01098 if ((request.from + len) > (OFFT_MAX)) {
01099 DEBUG("[Number too large!]");
01100 ERROR(client, reply);
01101 continue;
01102 }
01103
01104 if (((ssize_t)((off_t)request.from + len) > client->exportsize)) {
01105 DEBUG("[RANGE!]");
01106 ERROR(client, reply);
01107 continue;
01108 }
01109
01110 if (request.type==NBD_CMD_WRITE) {
01111 DEBUG("wr: net->buf, ");
01112 readit(client->net, buf, len);
01113 DEBUG("buf->exp, ");
01114 if ((client->server->flags & F_READONLY) ||
01115 (client->server->flags & F_AUTOREADONLY)) {
01116 DEBUG("[WRITE to READONLY!]");
01117 ERROR(client, reply);
01118 continue;
01119 }
01120 if (expwrite(request.from, buf, len, client)) {
01121 DEBUG("Write failed: %m" );
01122 ERROR(client, reply);
01123 continue;
01124 }
01125 SEND(client->net, reply);
01126 DEBUG("OK!\n");
01127 continue;
01128 }
01129
01130
01131 DEBUG("exp->buf, ");
01132 if (expread(request.from, buf + sizeof(struct nbd_reply), len, client)) {
01133 DEBUG("Read failed: %m");
01134 ERROR(client, reply);
01135 continue;
01136 }
01137
01138 DEBUG("buf->net, ");
01139 memcpy(buf, &reply, sizeof(struct nbd_reply));
01140 writeit(client->net, buf, len + sizeof(struct nbd_reply));
01141 DEBUG("OK!\n");
01142 }
01143 return 0;
01144 }
01145
01146
01147
01148
01149
01150
01151 void setupexport(CLIENT* client) {
01152 int i;
01153 off_t laststartoff = 0, lastsize = 0;
01154 int multifile = (client->server->flags & F_MULTIFILE);
01155
01156 client->export = g_array_new(TRUE, TRUE, sizeof(FILE_INFO));
01157
01158
01159
01160
01161 for(i=0; ; i++) {
01162 FILE_INFO fi;
01163 gchar *tmpname;
01164 mode_t mode = (client->server->flags & F_READONLY) ? O_RDONLY : O_RDWR;
01165
01166 if(multifile) {
01167 tmpname=g_strdup_printf("%s.%d", client->exportname, i);
01168 } else {
01169 tmpname=g_strdup(client->exportname);
01170 }
01171 DEBUG2( "Opening %s\n", tmpname );
01172 fi.fhandle = open(tmpname, mode);
01173 if(fi.fhandle == -1 && mode == O_RDWR) {
01174
01175 fi.fhandle = open(tmpname, O_RDONLY);
01176 if(fi.fhandle != -1) {
01177 client->server->flags |= F_AUTOREADONLY;
01178 client->server->flags |= F_READONLY;
01179 }
01180 }
01181 if(fi.fhandle == -1) {
01182 if(multifile && i>0)
01183 break;
01184 err("Could not open exported file: %m");
01185 }
01186 fi.startoff = laststartoff + lastsize;
01187 g_array_append_val(client->export, fi);
01188 g_free(tmpname);
01189
01190
01191
01192 laststartoff = fi.startoff;
01193 lastsize = size_autodetect(fi.fhandle);
01194
01195 if(!multifile)
01196 break;
01197 }
01198
01199
01200 client->exportsize = laststartoff + lastsize;
01201
01202
01203 if(client->server->expected_size) {
01204
01205 if(client->server->expected_size > client->exportsize) {
01206 err("Size of exported file is too big\n");
01207 }
01208
01209 client->exportsize = client->server->expected_size;
01210 }
01211
01212 msg3(LOG_INFO, "Size of exported file/device is %Lu", (unsigned long long)client->exportsize);
01213 if(multifile) {
01214 msg3(LOG_INFO, "Total number of files: %d", i);
01215 }
01216 }
01217
01218 int copyonwrite_prepare(CLIENT* client) {
01219 off_t i;
01220 if ((client->difffilename = malloc(1024))==NULL)
01221 err("Failed to allocate string for diff file name");
01222 snprintf(client->difffilename, 1024, "%s-%s-%d.diff",client->exportname,client->clientname,
01223 (int)getpid()) ;
01224 client->difffilename[1023]='\0';
01225 msg3(LOG_INFO,"About to create map and diff file %s",client->difffilename) ;
01226 client->difffile=open(client->difffilename,O_RDWR | O_CREAT | O_TRUNC,0600) ;
01227 if (client->difffile<0) err("Could not create diff file (%m)") ;
01228 if ((client->difmap=calloc(client->exportsize/DIFFPAGESIZE,sizeof(u32)))==NULL)
01229 err("Could not allocate memory") ;
01230 for (i=0;i<client->exportsize/DIFFPAGESIZE;i++) client->difmap[i]=(u32)-1 ;
01231
01232 return 0;
01233 }
01234
01235
01236
01237
01238
01239
01240
01241
01242
01243 void serveconnection(CLIENT *client) {
01244 setupexport(client);
01245
01246 if (client->server->flags & F_COPYONWRITE) {
01247 copyonwrite_prepare(client);
01248 }
01249
01250 setmysockopt(client->net);
01251
01252 mainloop(client);
01253 }
01254
01255
01256
01257
01258
01259
01260
01261
01262
01263
01264
01265
01266 void set_peername(int net, CLIENT *client) {
01267 struct sockaddr_in addrin;
01268 struct sockaddr_in netaddr;
01269 socklen_t addrinlen = sizeof( addrin );
01270 char *peername;
01271 char *netname;
01272 char *tmp;
01273 int i;
01274
01275 if (getpeername(net, (struct sockaddr *) &addrin, (socklen_t *)&addrinlen) < 0)
01276 err("getsockname failed: %m");
01277 peername = g_strdup(inet_ntoa(addrin.sin_addr));
01278 switch(client->server->virtstyle) {
01279 case VIRT_NONE:
01280 client->exportname=g_strdup(client->server->exportname);
01281 break;
01282 case VIRT_IPHASH:
01283 for(i=0;i<strlen(peername);i++) {
01284 if(peername[i]=='.') {
01285 peername[i]='/';
01286 }
01287 }
01288 case VIRT_IPLIT:
01289 client->exportname=g_strdup_printf(client->server->exportname, peername);
01290 break;
01291 case VIRT_CIDR:
01292 memcpy(&netaddr, &addrin, addrinlen);
01293 netaddr.sin_addr.s_addr>>=32-(client->server->cidrlen);
01294 netaddr.sin_addr.s_addr<<=32-(client->server->cidrlen);
01295 netname = inet_ntoa(netaddr.sin_addr);
01296 tmp=g_strdup_printf("%s/%s", netname, peername);
01297 client->exportname=g_strdup_printf(client->server->exportname, tmp);
01298 break;
01299 }
01300
01301 msg4(LOG_INFO, "connect from %s, assigned file is %s",
01302 peername, client->exportname);
01303 client->clientname=g_strdup(peername);
01304 g_free(peername);
01305 }
01306
01307
01308
01309
01310
01311 void destroy_pid_t(gpointer data) {
01312 g_free(data);
01313 }
01314
01315
01316
01317
01318
01319
01320
01321
01322 #if !defined(NODAEMON) && !defined(NOFORK)
01323 void daemonize(SERVER* serve) {
01324 FILE*pidf;
01325
01326
01327
01328
01329 if(daemon(0,0) < 0) {
01330 err("daemon");
01331 }
01332 if(!*pidftemplate) {
01333 if(serve) {
01334 strncpy(pidftemplate, "/var/run/nbd-server.%d.pid", 255);
01335 } else {
01336 strncpy(pidftemplate, "/var/run/nbd-server.pid", 255);
01337 }
01338 }
01339 snprintf(pidfname, 255, pidftemplate, serve ? serve->port : 0);
01340 pidf=fopen(pidfname, "w");
01341 if(pidf) {
01342 fprintf(pidf,"%d\n", (int)getpid());
01343 fclose(pidf);
01344 } else {
01345 perror("fopen");
01346 fprintf(stderr, "Not fatal; continuing");
01347 }
01348 }
01349 #else
01350 #define daemonize(serve)
01351 #endif
01352
01353
01354
01355
01356
01357
01358 void setup_serve(SERVER *serve) {
01359 struct sockaddr_in addrin;
01360 struct sigaction sa;
01361 int addrinlen = sizeof(addrin);
01362 int sock_flags;
01363 #ifndef sun
01364 int yes=1;
01365 #else
01366 char yes='1';
01367 #endif
01368 if ((serve->socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
01369 err("socket: %m");
01370
01371
01372 if (setsockopt(serve->socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
01373 err("setsockopt SO_REUSEADDR");
01374 }
01375 if (setsockopt(serve->socket,SOL_SOCKET,SO_KEEPALIVE,&yes,sizeof(int)) == -1) {
01376 err("setsockopt SO_KEEPALIVE");
01377 }
01378
01379
01380 if ((sock_flags = fcntl(serve->socket, F_GETFL, 0)) == -1) {
01381 err("fcntl F_GETFL");
01382 }
01383 if (fcntl(serve->socket, F_SETFL, sock_flags | O_NONBLOCK) == -1) {
01384 err("fcntl F_SETFL O_NONBLOCK");
01385 }
01386
01387 DEBUG("Waiting for connections... bind, ");
01388 addrin.sin_family = AF_INET;
01389 addrin.sin_port = htons(serve->port);
01390 addrin.sin_addr.s_addr = 0;
01391 if (bind(serve->socket, (struct sockaddr *) &addrin, addrinlen) < 0)
01392 err("bind: %m");
01393 DEBUG("listen, ");
01394 if (listen(serve->socket, 1) < 0)
01395 err("listen: %m");
01396 sa.sa_handler = sigchld_handler;
01397 sigemptyset(&sa.sa_mask);
01398 sa.sa_flags = SA_RESTART;
01399 if(sigaction(SIGCHLD, &sa, NULL) == -1)
01400 err("sigaction: %m");
01401 sa.sa_handler = sigterm_handler;
01402 sigemptyset(&sa.sa_mask);
01403 sa.sa_flags = SA_RESTART;
01404 if(sigaction(SIGTERM, &sa, NULL) == -1)
01405 err("sigaction: %m");
01406 }
01407
01408
01409
01410
01411 void setup_servers(GArray* servers) {
01412 int i;
01413
01414 for(i=0;i<servers->len;i++) {
01415 setup_serve(&(g_array_index(servers, SERVER, i)));
01416 }
01417 children=g_hash_table_new_full(g_int_hash, g_int_equal, NULL, destroy_pid_t);
01418 }
01419
01420
01421
01422
01423 int serveloop(GArray* servers) {
01424 struct sockaddr_in addrin;
01425 socklen_t addrinlen=sizeof(addrin);
01426 SERVER *serve;
01427 int i;
01428 int max;
01429 int sock;
01430 fd_set mset;
01431 fd_set rset;
01432 struct timeval tv;
01433
01434
01435
01436
01437
01438
01439
01440
01441 max=0;
01442 FD_ZERO(&mset);
01443 for(i=0;i<servers->len;i++) {
01444 sock=(g_array_index(servers, SERVER, i)).socket;
01445 FD_SET(sock, &mset);
01446 max=sock>max?sock:max;
01447 }
01448 for(;;) {
01449 CLIENT *client;
01450 int net;
01451 pid_t *pid;
01452
01453 memcpy(&rset, &mset, sizeof(fd_set));
01454 tv.tv_sec=0;
01455 tv.tv_usec=500;
01456 if(select(max+1, &rset, NULL, NULL, &tv)>0) {
01457 DEBUG("accept, ");
01458 for(i=0;i<servers->len;i++) {
01459 serve=&(g_array_index(servers, SERVER, i));
01460 if(FD_ISSET(serve->socket, &rset)) {
01461 if ((net=accept(serve->socket, (struct sockaddr *) &addrin, &addrinlen)) < 0)
01462 err("accept: %m");
01463
01464 client = g_malloc(sizeof(CLIENT));
01465 client->server=serve;
01466 client->exportsize=OFFT_MAX;
01467 client->net=net;
01468 set_peername(net, client);
01469 if (!authorized_client(client)) {
01470 msg2(LOG_INFO,"Unauthorized client") ;
01471 close(net);
01472 continue;
01473 }
01474 msg2(LOG_INFO,"Authorized client") ;
01475 pid=g_malloc(sizeof(pid_t));
01476 #ifndef NOFORK
01477 if ((*pid=fork())<0) {
01478 msg3(LOG_INFO,"Could not fork (%s)",strerror(errno)) ;
01479 close(net);
01480 continue;
01481 }
01482 if (*pid>0) {
01483 close(net);
01484 g_hash_table_insert(children, pid, pid);
01485 continue;
01486 }
01487
01488 g_hash_table_destroy(children);
01489 for(i=0; (i<servers->len && (serve=(g_array_index(servers, SERVER*, i)))); i++) {
01490 close(serve->socket);
01491 }
01492
01493
01494
01495
01496
01497
01498 g_array_free(servers, FALSE);
01499 #endif // NOFORK
01500 msg2(LOG_INFO,"Starting to serve");
01501 serveconnection(client);
01502 }
01503 }
01504 }
01505 }
01506 }
01507
01508
01509
01510
01511 void dousers(void) {
01512 struct passwd *pw;
01513 struct group *gr;
01514 if(runuser) {
01515 pw=getpwnam(runuser);
01516 if(setuid(pw->pw_uid)<0)
01517 msg3(LOG_DEBUG, "Could not set UID: %s", strerror(errno));
01518 }
01519 if(rungroup) {
01520 gr=getgrnam(rungroup);
01521 if(setgid(gr->gr_gid)<0)
01522 msg3(LOG_DEBUG, "Could not set GID: %s", strerror(errno));
01523 }
01524 }
01525
01526
01527
01528
01529 int main(int argc, char *argv[]) {
01530 SERVER *serve;
01531 GArray *servers;
01532 GError *err=NULL;
01533
01534 if (sizeof( struct nbd_request )!=28) {
01535 fprintf(stderr,"Bad size of structure. Alignment problems?\n");
01536 exit(-1) ;
01537 }
01538
01539 memset(pidftemplate, '\0', 256);
01540
01541 logging();
01542 config_file_pos = g_strdup(CFILE);
01543 serve=cmdline(argc, argv);
01544 servers = parse_cfile(config_file_pos, &err);
01545 if(!servers || !servers->len) {
01546 g_warning("Could not parse config file: %s", err->message);
01547 }
01548 if(serve) {
01549 g_array_append_val(servers, *serve);
01550
01551 if (!(serve->port)) {
01552 CLIENT *client;
01553 #ifndef ISSERVER
01554
01555
01556
01557
01558 close(1);
01559 close(2);
01560 open("/dev/null", O_WRONLY);
01561 open("/dev/null", O_WRONLY);
01562 #endif
01563 client=g_malloc(sizeof(CLIENT));
01564 client->server=serve;
01565 client->net=0;
01566 client->exportsize=OFFT_MAX;
01567 set_peername(0,client);
01568 serveconnection(client);
01569 return 0;
01570 }
01571 }
01572 if((!serve) && (!servers||!servers->len)) {
01573 g_message("Nothing to do! Bye!");
01574 exit(EXIT_FAILURE);
01575 }
01576 daemonize(serve);
01577 setup_servers(servers);
01578 dousers();
01579 serveloop(servers);
01580 return 0 ;
01581 }