r3: Initial revision
authorpavel <pavel>
Fri, 27 Oct 2000 07:41:13 +0000 (07:41 +0000)
committerpavel <pavel>
Fri, 27 Oct 2000 07:41:13 +0000 (07:41 +0000)
README [new file with mode: 0644]
cliserv.h [new file with mode: 0644]
config.h.in [new file with mode: 0644]
configure.in [new file with mode: 0644]
nbd-client.c [new file with mode: 0644]
nbd-server.c [new file with mode: 0644]

diff --git a/README b/README
new file mode 100644 (file)
index 0000000..d3d1073
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+Initial README
diff --git a/cliserv.h b/cliserv.h
new file mode 100644 (file)
index 0000000..b9557ff
--- /dev/null
+++ b/cliserv.h
@@ -0,0 +1,125 @@
+/* This header file is shared by client & server. They really have
+ * something to share...
+ * */
+
+/* Client/server protocol is as follows:
+   Send INIT_PASSWD
+   Send 64-bit cliserv_magic
+   Send 64-bit size of exported device
+   Send 128 bytes of zeros (reserved for future use)
+ */
+
+#include "config.h"
+#include <errno.h>
+#include <string.h>
+#include <netdb.h>
+
+#if SIZEOF_UNSIGNED_SHORT_INT==4
+typedef unsigned short u32;
+#elif SIZEOF_UNSIGNED_INT==4
+typedef unsigned int u32;
+#elif SIZEOF_UNSIGNED_LONG_INT==4
+typedef unsigned long u32;
+#else
+#error I need at least some 32-bit type
+#endif
+
+#ifndef        FS_32BIT
+#if SIZEOF_UNSIGNED_INT==8
+typedef unsigned int u64;
+#elif SIZEOF_UNSIGNED_LONG_INT==8
+typedef unsigned long u64;
+#elif SIZEOF_UNSIGNED_LONG_LONG_INT==8
+typedef unsigned long long u64;
+#else
+#error I need at least some 64-bit type
+#endif
+#endif /* FS_32BIT */
+
+#include "nbd.h"
+
+#ifdef FS_32BIT
+struct { unsigned char m[8]; } cliserv_magic = { { 0x00, 0x00, 0x42, 0x02, 0x81, 0x86, 0x12, 0x53 } };
+#else
+u64 cliserv_magic = 0x00420281861253LL;
+#endif
+#define INIT_PASSWD "NBDMAGIC"
+
+#define INFO(a) do { } while(0)
+
+void setmysockopt(int sock)
+{
+       int size = 1;
+#if 0
+       if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &size, sizeof(int)) < 0)
+                INFO("(no sockopt/1: %m)");
+#endif
+#ifdef IPPROTO_TCP
+       size = 1;
+       if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &size, sizeof(int)) < 0)
+                INFO("(no sockopt/2: %m)");
+#endif
+#if 0
+       size = 1024;
+       if (setsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, &size, sizeof(int)) < 0)
+                INFO("(no sockopt/3: %m)");
+#endif
+}
+
+void err(const char *s)
+{
+       const int maxlen = 150;
+       char s1[maxlen], *s2;
+       int n = 0;
+
+       strncpy(s1, s, maxlen);
+       if (s2 = strstr(s, "%m")) {
+               strcpy(s1 + (s2 - s), strerror(errno));
+               s2 += 2;
+               strcpy(s1 + strlen(s1), s2);
+       }
+#ifndef        sun
+       /* Solaris doesn't have %h in syslog */
+       else if (s2 = strstr(s, "%h")) {
+               strcpy(s1 + (s2 - s), hstrerror(h_errno));
+               s2 += 2;
+               strcpy(s1 + strlen(s1), s2);
+       }
+#endif
+
+       s1[maxlen-1] = '\0';
+#ifdef ISSERVER
+       syslog(LOG_ERR, s1);
+#else
+       fprintf(stderr, "Error: %s\n", s1);
+#endif
+       exit(1);
+}
+
+void logging(void)
+{
+#ifdef ISSERVER
+       openlog(MY_NAME, LOG_PID, LOG_DAEMON);
+#endif
+       setvbuf(stdout, NULL, _IONBF, 0);
+       setvbuf(stderr, NULL, _IONBF, 0);
+}
+
+#ifndef        FS_32BIT
+#ifdef WORDS_BIGENDIAN
+u64 ntohll(u64 a)
+{
+       return a;
+}
+#else
+u64 ntohll(u64 a)
+{
+       u32 lo = a & 0xffffffff;
+       u32 hi = a >> 32U;
+       lo = ntohl(lo);
+       hi = ntohl(hi);
+       return ((u64) lo) << 32U | hi;
+}
+#endif
+#define htonll ntohll
+#endif /* FS_32BIT */
diff --git a/config.h.in b/config.h.in
new file mode 100644 (file)
index 0000000..26f4655
--- /dev/null
@@ -0,0 +1,20 @@
+/* config.h.in.  Generated automatically from configure.in by autoheader.  */
+
+/* Define as __inline if that's what the C compiler calls it.  */
+#undef inline
+
+/* Define if your processor stores words with the most significant
+   byte first (like Motorola and SPARC, unlike Intel and VAX).  */
+#undef WORDS_BIGENDIAN
+
+/* The number of bytes in a unsigned int.  */
+#undef SIZEOF_UNSIGNED_INT
+
+/* The number of bytes in a unsigned long int.  */
+#undef SIZEOF_UNSIGNED_LONG_INT
+
+/* The number of bytes in a unsigned long long int.  */
+#undef SIZEOF_UNSIGNED_LONG_LONG_INT
+
+/* The number of bytes in a unsigned short int.  */
+#undef SIZEOF_UNSIGNED_SHORT_INT
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..7911379
--- /dev/null
@@ -0,0 +1,32 @@
+dnl Configure script for NBD system
+dnl (c) 1998 Martin Mares <mj@ucw.cz>, (c) 2000 Pavel Machek <pavel@ucw.cz>
+AC_INIT(nbd-server.c)
+
+
+AC_PROG_CC
+AC_MSG_CHECKING(whether warnings should be enabled)
+if test -n "$GCC" ; then
+  AC_MSG_RESULT(yes)
+  CFLAGS="$CFLAGS -Wall -W -Wno-parentheses -Wstrict-prototypes -Wno-unused"
+else
+  AC_MSG_RESULT(no)
+fi
+#AC_PROG_INSTALL
+AC_C_BIGENDIAN
+AC_C_INLINE
+AC_CHECK_SIZEOF(unsigned short int)
+AC_CHECK_SIZEOF(unsigned int)
+AC_CHECK_SIZEOF(unsigned long int)
+AC_CHECK_SIZEOF(unsigned long long int)
+AC_CHECK_FUNCS(llseek)
+AC_MSG_CHECKING(whether client should be built)
+case "`uname`" in
+     *Linux*) BUILD_CLIENT=nbd-client
+             AC_MSG_RESULT(yes)
+              ;;
+     *) AC_MSG_RESULT(no) ;;
+     esac
+AC_SUBST(BUILD_CLIENT)
+AC_CONFIG_HEADER(config.h)
+AC_OUTPUT(Makefile)
+
diff --git a/nbd-client.c b/nbd-client.c
new file mode 100644 (file)
index 0000000..dd9156c
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Open connection for network block device
+ *
+ * Copyright 1997,1998 Pavel Machek, distribute under GPL
+ *  <pavel@atrey.karlin.mff.cuni.cz>
+ *
+ * Version 1.0 - 64bit issues should be fixed, now
+ */
+
+/* I added new option '-d' to send the disconnect request */
+
+#include <asm/page.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>                /* sockaddr_in, htons, in_addr */
+#include <netdb.h>             /* hostent, gethostby*, getservby* */
+#include <stdio.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <stdlib.h>
+
+#define MY_NAME "nbd_client"
+#ifndef __GNUC__
+#error I need GCC to work
+#endif
+
+#include <linux/ioctl.h>
+#include "cliserv.h"
+
+int opennet(char *name, int port)
+{
+       int sock;
+       struct sockaddr_in xaddrin;
+       int xaddrinlen = sizeof(xaddrin);
+       struct hostent *hostn;
+
+       hostn = gethostbyname(name);
+       if (!hostn)
+               err("Gethostname failed: %h\n");
+
+       if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
+               err("Socket failed: %m");
+
+       xaddrin.sin_family = AF_INET;
+       xaddrin.sin_port = htons(port);
+       xaddrin.sin_addr.s_addr = *((int *) hostn->h_addr);
+       if ((connect(sock, (struct sockaddr *) &xaddrin, xaddrinlen) < 0))
+               err("Connect: %m");
+
+       setmysockopt(sock);
+       return sock;
+}
+
+int main(int argc, char *argv[])
+{
+       int port, sock, nbd, one = 1;
+       u64 magic, size64;
+       unsigned long size;
+       char buf[256] = "\0\0\0\0\0\0\0\0\0";
+       int swap = (argc > 4);
+
+       logging();
+
+       if (argc < 2) {
+       errmsg:
+               fprintf(stderr, "Usage: host port nbd_device -swap\n");
+               fprintf(stderr, "or     -d nbd_device \n");
+               return 1;
+       }
+
+       if (strcmp(argv[1],"-d")==0) {
+         nbd = open(argv[2], O_RDWR);
+         if (nbd < 0)
+               err("Can not open NBD: %m");
+         printf("Disconnecting: que, ");
+         if (ioctl(nbd, NBD_CLEAR_QUE)< 0)
+               err("Ioctl failed: %m\n");
+         printf("disconnect, ");
+#ifdef NBD_DISCONNECT
+         if (ioctl(nbd, NBD_DISCONNECT)<0)
+               err("Ioctl failed: %m\n");
+         printf("sock, ");
+#else
+         die("Can't disconnect: I was not compiled with disconnect support!\n" );
+#endif
+         if (ioctl(nbd, NBD_CLEAR_SOCK)<0)
+               err("Ioctl failed: %m\n");
+         printf("done\n");
+         return 0;
+       }
+       
+       if (argc<4) goto errmsg;
+       port = atoi(argv[2]);
+       sock = opennet(argv[1], port);
+       nbd = open(argv[3], O_RDWR);
+       if (nbd < 0)
+         err("Can not open NBD: %m");
+
+       printf("Negotiation: ");
+       if (read(sock, buf, 8) < 0)
+               err("Failed/1: %m");
+       if (strcmp(buf, INIT_PASSWD))
+               err("INIT_PASSWD bad");
+       printf(".");
+       if (read(sock, &magic, sizeof(magic)) < 0)
+               err("Failed/2: %m");
+       magic = ntohll(magic);
+       if (magic != cliserv_magic)
+               err("Not enough cliserv_magic");
+       printf(".");
+
+       if (read(sock, &size64, sizeof(size64)) < 0)
+               err("Failed/3: %m\n");
+       size64 = ntohll(size64);
+       if (size64 > (~0UL >> 1)) {
+#ifdef NBD_SET_SIZE_BLOCKS
+               if ((size64 >> 10) > (~0UL >> 1)) {
+                       printf("size = %luMB", (unsigned long)(size64>>20));
+                       err("Exported device is too big for me. Get 64-bit machine :-(\n");
+               } else
+                       printf("size = %luKB", (unsigned long)(size64>>10));
+#else
+               printf("size = %luKB", (unsigned long)(size64>>10));
+               err("Exported device is too big. Get 64-bit machine or newer kernel :-(\n");
+#endif
+       } else
+               printf("size = %lu", (unsigned long)(size64));
+
+       if (read(sock, &buf, 128) < 0)
+               err("Failed/4: %m\n");
+       printf("\n");
+
+       if (size64 > (~0UL >> 1)) {
+#ifdef NBD_SET_SIZE_BLOCKS
+               if ((size64 >> 10) > (~0UL >> 1))
+               /*
+                * If you really need NBDs larger than 2TB on 32-bit
+                * machines you can use blocksizes larger than 1kB
+                * - FIXME
+                */
+                       err("Device too large.\n");
+               else {
+                       int er;
+
+                       if (ioctl(nbd, NBD_SET_BLKSIZE, 1UL << 10) < 0)
+                               err("Ioctl/1.1a failed: %m\n");
+                       size = (unsigned long)(size64 >> 10);
+                       if ((er = ioctl(nbd, NBD_SET_SIZE_BLOCKS, size)) < 0)
+                               err("Ioctl/1.1b failed: %m\n");
+               }
+#else
+               err("Device too large.\n");
+#endif
+       } else {
+               size = (unsigned long)size64;
+               if (ioctl(nbd, NBD_SET_SIZE, size) < 0)
+                       err("Ioctl/1 failed: %m\n");
+       }
+       ioctl(nbd, NBD_CLEAR_SOCK);
+       if (ioctl(nbd, NBD_SET_SOCK, sock) < 0)
+               err("Ioctl/2 failed: %m\n");
+
+#ifndef SO_SWAPPING
+       if (swap)
+               err("You have to compile me on machine with swapping patch enabled in order to use it later.");
+#else
+       if (swap)
+               if (setsockopt(sock, SOL_SOCKET, SO_SWAPPING, &one, sizeof(int)) < 0)
+                        err("Could not enable swapping: %m");
+#endif
+
+       /* Go daemon */
+
+       chdir("/");
+       if (fork())
+               exit(0);
+
+       if (ioctl(nbd, NBD_DO_IT) < 0)
+               fprintf(stderr, "Kernel call returned: %m");
+       else
+               fprintf(stderr, "Kernel call returned.");
+       printf("Closing: que, ");
+       ioctl(nbd, NBD_CLEAR_QUE);
+       printf("sock, ");
+       ioctl(nbd, NBD_CLEAR_SOCK);
+       printf("done\n");
+       return 0;
+}
diff --git a/nbd-server.c b/nbd-server.c
new file mode 100644 (file)
index 0000000..9a90940
--- /dev/null
@@ -0,0 +1,570 @@
+/*
+ * Network Block Device - server
+ *
+ * Copyright 1996-1998 Pavel Machek, distribute under GPL
+ *  <pavel@atrey.karlin.mff.cuni.cz>
+ *
+ * Version 1.0 - hopefully 64-bit-clean
+ * Version 1.1 - merging enhancements from Josh Parsons, <josh@coombs.anu.edu.au>
+ * Version 1.2 - autodetect size of block devices, thanx to Peter T. Breuer" <ptb@it.uc3m.es>
+ * Version 1.5 - can compile on Unix systems that don't have 64 bit integer
+ *     type, or don't have 64 bit file offsets by defining FS_32BIT
+ *     in compile options for nbd-server *only*. This can be done
+ *     with make FSCHOICE=-DFS_32BIT nbd-server. (I don't have the
+ *     original autoconf input file, or I would make it a configure
+ *     option.) Ken Yap <ken@nlc.net.au>.
+ */
+
+#define VERSION "1.5"
+#define GIGA (1*1024*1024*1024)
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>                /* sockaddr_in, htons, in_addr */
+#include <netdb.h>             /* hostent, gethostby*, getservby* */
+#include <syslog.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <arpa/inet.h>
+#include <strings.h>
+
+#define _IO(a,b)
+// #define ISSERVER
+#define MY_NAME "nbd_server"
+
+/* Authorization file should contain lines with IP addresses of 
+   clients authorized to use the server. If it does not exist,
+   access is permitted. */
+#define AUTH_FILE "nbd_server.allow"
+
+#include "cliserv.h"
+#undef _IO
+/* Deep magic: ioctl.h defines _IO macro (at least on linux) */
+
+
+/* Debugging macros, now nothing goes to syslog unless you say ISSERVER */
+#ifdef ISSERVER
+#define msg2(a,b) syslog(a,b)
+#define msg3(a,b,c) syslog(a,b,c)
+#define msg4(a,b,c,d) syslog(a,b,c,d)
+#else
+#define msg2(a,b) do { fprintf(stderr,b) ; fputs("\n",stderr) ; } while(0) 
+#define msg3(a,b,c) do { fprintf(stderr,b,c); fputs("\n",stderr) ; } while(0) 
+#define msg4(a,b,c,d) do { fprintf(stderr,b,c,d); fputs("\n",stderr) ; } while(0)
+#endif
+
+
+#include <sys/ioctl.h>
+#include <sys/mount.h>         /* For BLKGETSIZE */
+
+#ifdef FS_32BIT
+typedef u32            fsoffset_t;
+#define htonll         htonl
+#define ntohll         ntohl
+#else
+typedef u64            fsoffset_t;
+#endif
+
+
+//#define DODBG
+#ifdef DODBG
+#define DEBUG( a ) printf( a )
+#define DEBUG2( a,b ) printf( a,b )
+#define DEBUG3( a,b,c ) printf( a,b,c )
+#else
+#define DEBUG( a )
+#define DEBUG2( a,b ) 
+#define DEBUG3( a,b,c ) 
+#endif
+
+#if    defined(HAVE_LLSEEK) && !defined(sun)
+/* Solaris already has llseek defined in unistd.h */
+extern long long llseek(unsigned int, long long, unsigned int);
+#endif
+
+void serveconnection(int net)  ;
+void set_peername(int net,char *clientname) ;
+
+#define LINELEN 256 
+char difffilename[256] ;
+
+int authorized_client(char *name)
+/* 0 - authorization refused, 1 - OK 
+  authorization file contains one line per machine, no wildcards
+*/
+{ FILE *f ;
+   
+  char line[LINELEN] ; 
+
+  if ((f=fopen(AUTH_FILE,"r"))==NULL)
+    { msg4(LOG_INFO,"Can't open authorization file %s (%s).",
+          AUTH_FILE,strerror(errno)) ;
+      return 1 ; 
+    }
+  
+  while (fgets(line,LINELEN,f)!=NULL) {
+    if (strncmp(line,name,strlen(name))==0) { fclose(f)  ; return 1 ; }
+  }
+  fclose(f) ;
+  return 0 ;
+}
+
+
+inline void readit(int f, void *buf, int len)
+{
+       int res;
+       while (len > 0) {
+               DEBUG("*");
+               if ((res = read(f, buf, len)) <= 0)
+                       err("Read failed: %m");
+               len -= res;
+               buf += res;
+       }
+}
+
+inline void writeit(int f, void *buf, int len)
+{
+       int res;
+       while (len > 0) {
+               DEBUG("+");
+               if ((res = write(f, buf, len)) <= 0)
+                       err("Write failed: %m");
+               len -= res;
+               buf += res;
+       }
+}
+
+int port;                      /* Port I'm listening at */
+char *exportname;              /* File I'm exporting */
+fsoffset_t exportsize = ~0, hunksize = ~0;     /* ...and its length */
+int flags = 0;
+int export[1024];
+int difffile=-1 ;
+u32 difffilelen=0 ; /* number of pages in difffile */
+u32 *difmap=NULL ;
+char clientname[256] ;
+
+
+#define DIFFPAGESIZE 4096 /* diff file uses those chunks */
+
+#define F_READONLY 1
+#define F_MULTIFILE 2 
+#define F_COPYONWRITE 4
+
+void cmdline(int argc, char *argv[])
+{
+       int i;
+
+       if (argc < 3) {
+               printf("This is nbd-server version " VERSION "\n");     
+               printf("Usage: port file_to_export [size][kKmM] [-r] [-m] [-c]\n"
+                      "        -r read only\n"
+                      "        -m multiple file\n"
+                      "        -c copy on write\n"
+                      "        if port is set to 0, stdin is used (for running from inetd)\n"
+                      "        if file_to_export contains '%%s', it is substituted with IP\n"
+                      "                address of machine trying to connect\n" );
+               exit(0);
+       }
+       port = atoi(argv[1]);
+       for (i = 3; i < argc; i++) {
+               if (*argv[i] == '-') {
+                       switch (argv[i][1]) {
+                       case 'r':
+                               flags |= F_READONLY;
+                               break;
+                       case 'm':
+                               flags |= F_MULTIFILE;
+                               hunksize = 1*GIGA;
+                               break;
+                       case 'c': flags |=F_COPYONWRITE ;
+                               break ;
+                       }
+               } else {
+                       fsoffset_t es;
+                       int last = strlen(argv[i])-1;
+                       char suffix = argv[i][last];
+                       if (suffix == 'k' || suffix == 'K' ||
+                           suffix == 'm' || suffix == 'M')
+                               argv[i][last] = '\0';
+                       es = (fsoffset_t)atol(argv[i]);
+                       switch (suffix) {
+                               case 'm':
+                               case 'M':  es <<= 10;
+                               case 'k':
+                               case 'K':  es <<= 10;
+                               default :  break;
+                       }
+                       exportsize = es;
+               }
+       }
+
+       exportname = argv[2];
+}
+
+void connectme(int port)
+{
+       struct sockaddr_in addrin;
+       int addrinlen = sizeof(addrin);
+       int net, sock, newpid;
+
+       if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
+               err("socket: %m");
+
+       DEBUG("Waiting for connections... bind, ");
+       addrin.sin_family = AF_INET;
+       addrin.sin_port = htons(port);
+       addrin.sin_addr.s_addr = 0;
+       if (bind(sock, (struct sockaddr *) &addrin, addrinlen) < 0)
+               err("bind: %m");
+       DEBUG("listen, ");
+       if (listen(sock, 1) < 0)
+               err("listen: %m");
+       DEBUG("accept, ");
+       for(;;) { /* infinite loop */
+         if ((net = accept(sock, (struct sockaddr *) &addrin, &addrinlen)) < 0)
+           err("accept: %m");
+
+         set_peername(net,clientname) ;
+         if (!authorized_client(clientname)) {
+           msg2(LOG_INFO,"Unauthorized client") ;
+           close(net) ;
+           continue ;
+         }
+         msg2(LOG_INFO,"Authorized client") ;
+         if ((newpid=fork())<0) {
+           msg3(LOG_INFO,"Could not fork (%s)",strerror(errno)) ;
+           close(net) ;
+           continue ;
+         }
+         if (newpid>0) { /* parent */
+           close(net) ; continue ; }
+         /* child */
+         close(sock) ;
+         msg2(LOG_INFO,"Starting to serve") ;
+         serveconnection(net) ;        
+       }
+}
+
+#define SEND writeit( net, &reply, sizeof( reply ));
+#define ERROR { reply.error = htonl(-1); SEND; reply.error = 0; lastpoint = -1; }
+
+fsoffset_t lastpoint = -1;
+
+void maybeseek(int handle, fsoffset_t a)
+{
+       if (a > exportsize)
+               err("Can not happen\n");
+       if (lastpoint != a) {
+#if    defined(HAVE_LLSEEK) && !defined(FS_32BIT)
+               if (llseek(handle, a, SEEK_SET) < 0)
+#else
+               if (lseek(handle, (long)a, SEEK_SET) < 0)
+#endif
+                       err("Can not seek locally!\n");
+               lastpoint = a;
+       } else {
+               DEBUG("@");
+       }
+}
+
+void myseek(int handle,fsoffset_t a)
+{
+#if HAVE_LLSEEK && !defined(FS_32BIT)
+  if (llseek(handle, a, SEEK_SET) < 0)
+#else
+  if (lseek(handle, (long)a, SEEK_SET) < 0)
+#endif 
+    err("Can not seek locally!\n");
+}
+
+char pagebuf[DIFFPAGESIZE] ;
+
+
+int rawexpread(fsoffset_t a, char *buf, int len)
+{
+  maybeseek(export[a/hunksize], a%hunksize);
+  return (read(export[a/hunksize], buf, len) != len);
+}
+
+int expread(fsoffset_t a, char *buf, int len)
+{ int rdlen ; fsoffset_t mapcnt,mapl,maph ;
+  fsoffset_t pagestart; int offset ;
+  if (flags & F_COPYONWRITE) {
+    DEBUG3("Asked to read %d bytes at %lu.\n",len,(unsigned long)a) ;
+
+    mapl=a/DIFFPAGESIZE ; maph=(a+len-1)/DIFFPAGESIZE ;
+
+    for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
+      pagestart=mapcnt*DIFFPAGESIZE ;
+      offset=a-pagestart ;
+      rdlen=(len<DIFFPAGESIZE-offset) ? len : DIFFPAGESIZE-offset ;
+      if (difmap[mapcnt]!=(u32)(-1)) { /* the block is already there */
+       DEBUG3("Page %d is at %ld\n",(int)mapcnt,(long int)difmap[mapcnt]) ;
+       myseek(difffile,difmap[mapcnt]*DIFFPAGESIZE+offset) ;
+       if (read(difffile, buf, rdlen) != rdlen) return -1 ;
+      } else { /* the block is not there */
+       DEBUG2("Page %d is not here, we read the original one\n",
+             (int)mapcnt) ;
+       if (rawexpread(a,buf,rdlen)) return -1 ;
+      }
+      len-=rdlen ; a+=rdlen ; buf+=rdlen ;
+    }
+  } else return rawexpread(a,buf,len) ;
+  return 0 ;
+}
+
+int rawexpwrite(fsoffset_t a, char *buf, int len)
+{
+       maybeseek(export[a/hunksize], a%hunksize);
+       return (write(export[a/hunksize], buf, len) != len);
+}
+
+
+int expwrite(fsoffset_t a, char *buf, int len)
+{  u32 mapcnt,mapl,maph ; int wrlen,rdlen ; 
+   fsoffset_t pagestart ; int offset ;
+
+  if (flags & F_COPYONWRITE) {
+    DEBUG3("Asked to write %d bytes at %lu.\n",len,(unsigned long)a) ;
+
+    mapl=a/DIFFPAGESIZE ; maph=(a+len-1)/DIFFPAGESIZE ;
+
+    for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
+      pagestart=mapcnt*DIFFPAGESIZE ;
+      offset=a-pagestart ;
+      wrlen=(len<DIFFPAGESIZE-offset) ? len : DIFFPAGESIZE-offset ;
+
+      if (difmap[mapcnt]!=(u32)(-1)) { /* the block is already there */
+       DEBUG3("Page %d is at %ld\n",mapcnt,(long)difmap[mapcnt]) ;
+       myseek(difffile,difmap[mapcnt]*DIFFPAGESIZE+offset) ;
+       if (write(difffile, buf, wrlen) != wrlen) return -1 ;
+      } else { /* the block is not there */
+       myseek(difffile,difffilelen*DIFFPAGESIZE) ;
+       difmap[mapcnt]=difffilelen++ ;
+       DEBUG3("Page %d is not here, we put it at %ld\n",
+             mapcnt,(long)difmap[mapcnt]) ;
+
+       rdlen=DIFFPAGESIZE ;
+       if (rdlen+pagestart%hunksize>hunksize) 
+         rdlen=hunksize-(pagestart%hunksize) ;
+       if (rawexpread(pagestart,pagebuf,rdlen)) return -1 ;
+       memcpy(pagebuf+offset,buf,wrlen) ;
+       if (write(difffile,pagebuf,DIFFPAGESIZE)!=DIFFPAGESIZE) return -1 ;
+      }                                                    
+      len-=wrlen ; a+=wrlen ; buf+=wrlen ;
+    }
+  } else return(rawexpwrite(a,buf,len)); 
+  return 0 ;
+}
+
+int mainloop(int net)
+{
+       struct nbd_request request;
+       struct nbd_reply reply;
+       char zeros[300];
+       int i = 0;
+       fsoffset_t size_host;
+
+       memset(zeros, 0, 290);
+       if (write(net, INIT_PASSWD, 8) < 0)
+               err("Negotiation failed: %m");
+#ifndef        FS_32BIT
+       cliserv_magic = htonll(cliserv_magic);
+#endif
+       if (write(net, &cliserv_magic, sizeof(cliserv_magic)) < 0)
+               err("Negotiation failed: %m");
+       size_host = htonll(exportsize);
+#ifdef FS_32BIT
+        if (write(net, zeros, 4) < 0 || write(net, &size_host, 4) < 0)
+#else
+       if (write(net, &size_host, 8) < 0)
+#endif
+               err("Negotiation failed: %m");
+       if (write(net, zeros, 128) < 0)
+               err("Negotiation failed: %m");
+
+       DEBUG("Entering request loop!\n");
+       reply.magic = htonl(NBD_REPLY_MAGIC);
+       reply.error = 0;
+       while (1) {
+               char buf[20480];
+               int len;
+
+#ifdef DODBG
+               i++;
+               printf("%d: ", i);
+#endif
+
+               readit(net, &request, sizeof(request));
+               request.from = ntohll(request.from);
+               request.type = ntohl(request.type);
+
+               if (request.type==2) { /* Disconnect request */
+                 if (difmap) free(difmap) ;
+                  if (difffile>=0) { 
+                     close(difffile) ; unlink(difffilename) ; }
+                 err("Disconnect request received.") ;
+               }
+
+               len = ntohl(request.len);
+
+               if (request.magic != htonl(NBD_REQUEST_MAGIC))
+                       err("Not enough magic.");
+               if (len > 10240)
+                       err("Request too big!");
+#ifdef DODBG
+               printf("%s from %d (%d) len %d, ", (request.type ? "WRITE" : "READ"),
+                      (int) request.from, (int) request.from / 512, len);
+#endif
+               memcpy(reply.handle, request.handle, sizeof(reply.handle));
+               if (((request.from + len) > exportsize) ||
+                   ((flags & F_READONLY) && request.type)) {
+                       DEBUG("[RANGE!]");
+                       ERROR;
+                       continue;
+               }
+               if (request.type==1) {  /* WRITE */
+                       DEBUG("wr: net->buf, ");
+                       readit(net, buf, len);
+                       DEBUG("buf->exp, ");
+                       if (expwrite(request.from, buf, len)) {
+                               DEBUG("Write failed: %m" );
+                               ERROR;
+                               continue;
+                       }
+                       lastpoint += len;
+                       SEND;
+                       continue;
+               }
+               /* READ */
+
+               DEBUG("exp->buf, ");
+               if (expread(request.from, buf + sizeof(struct nbd_reply), len)) {
+                       lastpoint = -1;
+                       DEBUG("Read failed: %m");
+                       ERROR;
+                       continue;
+               }
+               lastpoint += len;
+
+               DEBUG("buf->net, ");
+               memcpy(buf, &reply, sizeof(struct nbd_reply));
+               writeit(net, buf, len + sizeof(struct nbd_reply));
+               DEBUG("OK!\n");
+       }
+}
+
+char exportname2[1024];
+
+void set_peername(int net,char *clientname)
+{
+       struct sockaddr_in addrin;
+       int addrinlen = sizeof( addrin );
+       char *peername ;
+
+       if (getpeername( net, (struct sockaddr *) &addrin, &addrinlen ) < 0)
+               err("getsockname failed: %m");
+       peername = inet_ntoa(addrin.sin_addr);
+       sprintf(exportname2, exportname, peername);
+
+       msg4(LOG_INFO, "connect from %s, assigned file is %s", peername, exportname2);
+       strncpy(clientname,peername,255) ;
+}
+
+fsoffset_t size_autodetect(int export)
+{
+       fsoffset_t es;
+       DEBUG("looking for export size with lseek SEEK_END\n");
+       if ((int)(es = lseek(export, 0, SEEK_END)) == -1 || es == 0) {
+               struct stat stat_buf = { 0, } ;
+               int error;
+               DEBUG("looking for export size with fstat\n");
+               if ((error = fstat(export, &stat_buf)) == -1 || stat_buf.st_size == 0 ) {
+                       DEBUG("looking for export size with ioctl BLKGETSIZE\n");
+#ifdef BLKGETSIZE
+                       if(ioctl(export, BLKGETSIZE, &es) || es == 0) {
+#else
+                       if(1){
+#endif
+                               err("Could not find size of exported block device: %m");
+                       } else {
+                               es *= 512; /* assume blocksize 512 */
+                       }
+               } else {
+                       es = stat_buf.st_size;
+               }
+       }
+       return es;
+}
+
+int main(int argc, char *argv[])
+{
+       int net;
+       fsoffset_t i;
+
+       if (sizeof( struct nbd_request )!=28) {
+               fprintf(stderr,"Bad size of structure. Alignment problems?\n");
+               exit(-1) ;
+       }
+       logging();
+       cmdline(argc, argv);
+       
+       if (!port) return 1 ;
+       connectme(port); /* serve infinitely */
+       return 0 ;
+}
+
+
+void serveconnection(int net) 
+{   
+  u64 i ;
+
+  for (i=0; i<exportsize; i+=hunksize) {
+    char exportname3[1024];
+    
+    sprintf(exportname3, exportname2, i/hunksize);
+    printf( "Opening %s\n", exportname3 );
+    if ((export[i/hunksize] = open(exportname3, (flags & F_READONLY) ? O_RDONLY : O_RDWR)) == -1)
+      err("Could not open exported file: %m");
+    }
+       
+    if (exportsize == (u64)~0) {
+      exportsize = size_autodetect(export[0]);
+    }
+    if (exportsize > (~0UL >> 1))
+#ifdef HAVE_LLSEEK
+    if ((exportsize >> 10) > (~0UL >> 1))
+      msg3(LOG_INFO, "size of exported file/device is %luMB",
+              (unsigned long)(exportsize >> 20));
+    else
+      msg3(LOG_INFO, "size of exported file/device is %luKB",
+          (unsigned long)(exportsize >> 10));
+#else
+    err("Size of exported file is too big\n");
+#endif
+    else
+      msg3(LOG_INFO, "size of exported file/device is %lu",
+                      (unsigned long)exportsize);
+
+    if (flags & F_COPYONWRITE) {
+      sprintf(difffilename,"%s-%s-%d.diff",exportname2,clientname,
+             (int)getpid()) ;
+      msg3(LOG_INFO,"About to create map and diff file %s",difffilename) ;
+      difffile=open(difffilename,O_RDWR | O_CREAT | O_TRUNC,0600) ;
+      if (difffile<0) err("Could not create diff file (%m)") ;
+      if ((difmap=calloc(exportsize/DIFFPAGESIZE,sizeof(u32)))==NULL)
+         err("Could not allocate memory") ;
+      for (i=0;i<exportsize/DIFFPAGESIZE;i++) difmap[i]=(u32)-1 ;        
+    }
+    
+    setmysockopt(net);
+      
+    mainloop(net);
+}