From 6a58a88c8ed56f6e3193fec8efe9c6f94e60dc01 Mon Sep 17 00:00:00 2001 From: pavel Date: Fri, 27 Oct 2000 07:41:13 +0000 Subject: [PATCH] r3: Initial revision --- README | 1 + cliserv.h | 125 +++++++++++++ config.h.in | 20 +++ configure.in | 32 ++++ nbd-client.c | 191 ++++++++++++++++++++ nbd-server.c | 570 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 939 insertions(+) create mode 100644 README create mode 100644 cliserv.h create mode 100644 config.h.in create mode 100644 configure.in create mode 100644 nbd-client.c create mode 100644 nbd-server.c diff --git a/README b/README new file mode 100644 index 0000000..d3d1073 --- /dev/null +++ b/README @@ -0,0 +1 @@ +Initial README diff --git a/cliserv.h b/cliserv.h new file mode 100644 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 +#include +#include + +#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 index 0000000..26f4655 --- /dev/null +++ b/config.h.in @@ -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 index 0000000..7911379 --- /dev/null +++ b/configure.in @@ -0,0 +1,32 @@ +dnl Configure script for NBD system +dnl (c) 1998 Martin Mares , (c) 2000 Pavel Machek +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 index 0000000..dd9156c --- /dev/null +++ b/nbd-client.c @@ -0,0 +1,191 @@ +/* + * Open connection for network block device + * + * Copyright 1997,1998 Pavel Machek, distribute under GPL + * + * + * Version 1.0 - 64bit issues should be fixed, now + */ + +/* I added new option '-d' to send the disconnect request */ + +#include +#include +#include +#include +#include +#include +#include /* sockaddr_in, htons, in_addr */ +#include /* hostent, gethostby*, getservby* */ +#include +#include +#include +#include + +#define MY_NAME "nbd_client" +#ifndef __GNUC__ +#error I need GCC to work +#endif + +#include +#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 index 0000000..9a90940 --- /dev/null +++ b/nbd-server.c @@ -0,0 +1,570 @@ +/* + * Network Block Device - server + * + * Copyright 1996-1998 Pavel Machek, distribute under GPL + * + * + * Version 1.0 - hopefully 64-bit-clean + * Version 1.1 - merging enhancements from Josh Parsons, + * Version 1.2 - autodetect size of block devices, thanx to Peter T. Breuer" + * 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 . + */ + +#define VERSION "1.5" +#define GIGA (1*1024*1024*1024) + +#include +#include +#include +#include +#include /* sockaddr_in, htons, in_addr */ +#include /* hostent, gethostby*, getservby* */ +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include /* 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=(lenhunksize) + 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 (~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