e19a381fd918fe7c497024a45b8158da13b7e4c9
[nbd.git] / nbd-server.c
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 2002 Anton Altaparmakov <aia21@cam.ac.uk>
7  *
8  * Version 1.0 - hopefully 64-bit-clean
9  * Version 1.1 - merging enhancements from Josh Parsons, <josh@coombs.anu.edu.au>
10  * Version 1.2 - autodetect size of block devices, thanx to Peter T. Breuer" <ptb@it.uc3m.es>
11  * Version 1.5 - can compile on Unix systems that don't have 64 bit integer
12  *      type, or don't have 64 bit file offsets by defining FS_32BIT
13  *      in compile options for nbd-server *only*. This can be done
14  *      with make FSCHOICE=-DFS_32BIT nbd-server. (I don't have the
15  *      original autoconf input file, or I would make it a configure
16  *      option.) Ken Yap <ken@nlc.net.au>.
17  * Version 1.6 - fix autodetection of block device size and really make 64 bit
18  *      clean on 32 bit machines. Anton Altaparmakov <aia21@cam.ac.uk>
19  * Version 2.0 - Version synchronised with client
20  * Version 2.1 - Reap zombie client processes when they exit. Removed
21  *      (uncommented) the _IO magic, it's no longer necessary. Wouter
22  *      Verhelst <wouter@debian.org>
23  * Version 2.2 - Auto switch to read-only mode (usefull for floppies).
24  * Version 2.3 - Fixed code so that Large File Support works. This
25  *      removes the FS_32BIT compile-time directive; define
26  *      _FILE_OFFSET_BITS=64 and _LARGEFILE_SOURCE if you used to be
27  *      using FS_32BIT. This will allow you to use files >2GB instead of
28  *      having to use the -m option. Wouter Verhelst <wouter@debian.org>
29  * Version 2.4 - Added code to keep track of children, so that we can
30  *      properly kill them from initscripts. Add a call to daemon(),
31  *      so that processes don't think they have to wait for us, which is
32  *      interesting for initscripts as well. Wouter Verhelst
33  *      <wouter@debian.org>
34  */
35
36 #define VERSION PACKAGE_VERSION
37 #define GIGA (1*1024*1024*1024)
38
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <sys/stat.h>
42 #include <sys/wait.h>           /* wait */
43 #include <signal.h>             /* sigaction */
44 #include <netinet/tcp.h>
45 #include <netinet/in.h>         /* sockaddr_in, htons, in_addr */
46 #include <netdb.h>              /* hostent, gethostby*, getservby* */
47 #include <syslog.h>
48 #include <unistd.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <fcntl.h>
53 #include <arpa/inet.h>
54 #include <strings.h>
55
56 //#define _IO(a,b)
57 // #define ISSERVER
58 #define MY_NAME "nbd_server"
59
60 /* Authorization file should contain lines with IP addresses of 
61    clients authorized to use the server. If it does not exist,
62    access is permitted. */
63 #define AUTH_FILE "nbd_server.allow"
64 /* how much space for child PIDs we have by default. Dynamically
65    allocated, and will be realloc()ed if out of space, so this should
66    probably be fair for most situations. */
67 #define DEFAULT_CHILD_ARRAY 256
68
69 #include "cliserv.h"
70 //#undef _IO
71 /* Deep magic: ioctl.h defines _IO macro (at least on linux) */
72
73
74 /* Debugging macros, now nothing goes to syslog unless you say ISSERVER */
75 #ifdef ISSERVER
76 #define msg2(a,b) syslog(a,b)
77 #define msg3(a,b,c) syslog(a,b,c)
78 #define msg4(a,b,c,d) syslog(a,b,c,d)
79 #else
80 #define msg2(a,b) do { fprintf(stderr,b) ; fputs("\n",stderr) ; } while(0) 
81 #define msg3(a,b,c) do { fprintf(stderr,b,c); fputs("\n",stderr) ; } while(0) 
82 #define msg4(a,b,c,d) do { fprintf(stderr,b,c,d); fputs("\n",stderr) ; } while(0)
83 #endif
84
85
86 #include <sys/ioctl.h>
87 #include <sys/mount.h>          /* For BLKGETSIZE */
88
89 //#define DODBG
90 #ifdef DODBG
91 #define DEBUG( a ) printf( a )
92 #define DEBUG2( a,b ) printf( a,b )
93 #define DEBUG3( a,b,c ) printf( a,b,c )
94 #else
95 #define DEBUG( a )
96 #define DEBUG2( a,b ) 
97 #define DEBUG3( a,b,c ) 
98 #endif
99
100 void serveconnection(int net);
101 void set_peername(int net,char *clientname);
102
103 #define LINELEN 256 
104 char difffilename[256];
105 unsigned int timeout = 0;
106 int autoreadonly = 0;
107
108 int authorized_client(char *name)
109 /* 0 - authorization refused, 1 - OK 
110   authorization file contains one line per machine, no wildcards
111 */
112 { FILE *f ;
113    
114   char line[LINELEN] ; 
115
116   if ((f=fopen(AUTH_FILE,"r"))==NULL)
117     { msg4(LOG_INFO,"Can't open authorization file %s (%s).",
118            AUTH_FILE,strerror(errno)) ;
119       return 1 ; 
120     }
121   
122   while (fgets(line,LINELEN,f)!=NULL) {
123     if (strncmp(line,name,strlen(name))==0) { fclose(f)  ; return 1 ; }
124   }
125   fclose(f) ;
126   return 0 ;
127 }
128
129 inline void readit(int f, void *buf, int len)
130 {
131         int res;
132         while (len > 0) {
133                 DEBUG("*");
134                 if ((res = read(f, buf, len)) <= 0)
135                         err("Read failed: %m");
136                 len -= res;
137                 buf += res;
138         }
139 }
140
141 inline void writeit(int f, void *buf, int len)
142 {
143         int res;
144         while (len > 0) {
145                 DEBUG("+");
146                 if ((res = send(f, buf, len, 0)) <= 0)
147                         err("Send failed: %m");
148                 len -= res;
149                 buf += res;
150         }
151 }
152
153 /* This is starting to get ugly. If someone knows a better way to find
154  * the maximum value of a signed type *without* relying on overflow
155  * (doing so breaks on 64bit architectures), that would be nice.
156  */
157 #define OFFT_MAX (((((off_t)1)<<((sizeof(off_t)-1)*8))-1)<<7)+127
158 int port;                       /* Port I'm listening at */
159 char *exportname;               /* File I'm exporting */
160 off_t exportsize = OFFT_MAX;    /* ...and its length */
161 off_t hunksize = OFFT_MAX;
162 int flags = 0;
163 int export[1024];
164 int difffile=-1 ;
165 u32 difffilelen=0 ; /* number of pages in difffile */
166 u32 *difmap=NULL ;
167 char clientname[256] ;
168 int child_arraysize=DEFAULT_CHILD_ARRAY;
169 pid_t *children;
170
171 #define DIFFPAGESIZE 4096 /* diff file uses those chunks */
172
173 #define F_READONLY 1
174 #define F_MULTIFILE 2 
175 #define F_COPYONWRITE 4
176
177 void cmdline(int argc, char *argv[])
178 {
179         int i;
180
181         if (argc < 3) {
182                 printf("This is nbd-server version " VERSION "\n");     
183                 printf("Usage: port file_to_export [size][kKmM] [-r] [-m] [-c] [-a timeout_sec]\n"
184                        "        -r read only\n"
185                        "        -m multiple file\n"
186                        "        -c copy on write\n"
187                        "        -a maximum idle seconds, terminates when idle time exceeded\n"
188                        "        if port is set to 0, stdin is used (for running from inetd)\n"
189                        "        if file_to_export contains '%%s', it is substituted with IP\n"
190                        "                address of machine trying to connect\n" );
191                 exit(0);
192         }
193         port = atoi(argv[1]);
194         for (i = 3; i < argc; i++) {
195                 if (*argv[i] == '-') {
196                         switch (argv[i][1]) {
197                         case 'r':
198                                 flags |= F_READONLY;
199                                 break;
200                         case 'm':
201                                 flags |= F_MULTIFILE;
202                                 hunksize = 1*GIGA;
203                                 break;
204                         case 'c': flags |=F_COPYONWRITE;
205                                 break;
206                         case 'a': 
207                                 if (i+1<argc) {
208                                         timeout = atoi(argv[i+1]);
209                                         i++;
210                                 } else {
211                                         fprintf(stderr, "timeout requires argument\n");
212                                         exit(1);
213                                 }
214                         }
215                 } else {
216                         off_t es;
217                         int last = strlen(argv[i])-1;
218                         char suffix = argv[i][last];
219                         if (suffix == 'k' || suffix == 'K' ||
220                             suffix == 'm' || suffix == 'M')
221                                 argv[i][last] = '\0';
222                         es = (off_t)atol(argv[i]);
223                         switch (suffix) {
224                                 case 'm':
225                                 case 'M':  es <<= 10;
226                                 case 'k':
227                                 case 'K':  es <<= 10;
228                                 default :  break;
229                         }
230                         exportsize = es;
231                 }
232         }
233
234         exportname = argv[2];
235 }
236
237 void sigchld_handler(int s)
238 {
239         int* status=NULL;
240         int i;
241         pid_t pid;
242
243         while((pid=wait(status)) > 0) {
244                 if(WIFEXITED(status)) {
245                         msg3(LOG_INFO, "Child exited with %d", WEXITSTATUS(status));
246                 }
247                 for(i=0;children[i]!=pid&&i<child_arraysize;i++);
248                 if(i>=child_arraysize) {
249                         msg3(LOG_INFO, "SIGCHLD received for an unknown child with PID %ld",(long) pid);
250                 } else {
251                         children[i]=(pid_t)0;
252                         DEBUG2("Removing %d from the list of children", pid);
253                 }
254         }
255 }
256
257 /* If we are terminated, make sure our children are, too. */
258 void sigterm_handler(int s) {
259         int i;
260
261         for(i=0;i<child_arraysize;i++) {
262                 if(children[i]) {
263                         kill(children[i], s);
264                 }
265         }
266
267         exit(0);
268 }
269
270 void connectme(int port)
271 {
272         struct sockaddr_in addrin;
273         struct sigaction sa;
274         int addrinlen = sizeof(addrin);
275         int net, sock, newpid, i;
276 #ifndef sun
277         int yes=1;
278 #else
279         char yes='1';
280 #endif
281
282         if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
283                 err("socket: %m");
284
285         /* lose the pesky "Address already in use" error message */
286         if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
287                 err("setsockopt");
288         }
289
290         DEBUG("Waiting for connections... bind, ");
291         addrin.sin_family = AF_INET;
292         addrin.sin_port = htons(port);
293         addrin.sin_addr.s_addr = 0;
294         if (bind(sock, (struct sockaddr *) &addrin, addrinlen) < 0)
295                 err("bind: %m");
296         DEBUG("listen, ");
297         if (listen(sock, 1) < 0)
298                 err("listen: %m");
299         DEBUG("accept, ");
300         sa.sa_handler = sigchld_handler;
301         sigemptyset(&sa.sa_mask);
302         sa.sa_flags = SA_RESTART;
303         if(sigaction(SIGCHLD, &sa, NULL) == -1)
304                 err("sigaction: %m");
305         sa.sa_handler = sigterm_handler;
306         sigemptyset(&sa.sa_mask);
307         sa.sa_flags = SA_RESTART;
308         if(sigaction(SIGTERM, &sa, NULL) == -1)
309                 err("sigaction: %m");
310         children=malloc(sizeof(pid_t)*child_arraysize);
311         memset(children, 0, sizeof(pid_t)*DEFAULT_CHILD_ARRAY);
312 #ifndef NODAEMON
313 #ifndef NOFORK
314         if(daemon(1,0)<0) {
315                 err("daemon");
316         }
317 #endif /* NOFORK */
318 #endif /* NODAEMON */
319         for(;;) { /* infinite loop */
320                 if ((net = accept(sock, (struct sockaddr *) &addrin, &addrinlen)) < 0)
321                         err("accept: %m");
322                 
323                 set_peername(net,clientname) ;
324                 if (!authorized_client(clientname)) {
325                         msg2(LOG_INFO,"Unauthorized client") ;
326                         close(net) ;
327                         continue ;
328                 }
329                 msg2(LOG_INFO,"Authorized client") ;
330                 for(i=0;children[i]&&i<child_arraysize;i++);
331                 if(i>=child_arraysize) {
332                         realloc(children, sizeof(pid_t)*child_arraysize);
333                         memset(children+child_arraysize, 0, sizeof(pid_t)*DEFAULT_CHILD_ARRAY);
334                         i=child_arraysize+1;
335                         child_arraysize+=DEFAULT_CHILD_ARRAY;
336                 }
337 #ifndef NOFORK
338                 if ((children[i]=fork())<0) {
339                         msg3(LOG_INFO,"Could not fork (%s)",strerror(errno)) ;
340                         close(net) ;
341                         continue ;
342                 }
343                 if (children[i]>0) { /* parent */
344                         close(net) ; continue ; }
345                 /* child */
346                 realloc(children,0);
347                 close(sock) ;
348 #endif // NOFORK
349                 msg2(LOG_INFO,"Starting to serve") ;
350                 serveconnection(net) ;        
351         }
352 }
353
354 #define SEND writeit( net, &reply, sizeof( reply ));
355 #define ERROR { reply.error = htonl(-1); SEND; reply.error = 0; lastpoint = -1; }
356
357 off_t lastpoint = (off_t)-1;
358
359 void maybeseek(int handle, off_t a)
360 {
361 if (a > exportsize)
362         err("Can not happen\n");
363 if (lastpoint != a) {
364         if (lseek(handle, a, SEEK_SET) < 0)
365                 err("Can not seek locally!\n");
366         lastpoint = a;
367 } else {
368         DEBUG("@");
369 }
370 }
371
372 void myseek(int handle,off_t a)
373 {
374         if (lseek(handle, a, SEEK_SET) < 0)
375                 err("Can not seek locally!\n");
376 }
377
378 char pagebuf[DIFFPAGESIZE];
379
380 int rawexpread(off_t a, char *buf, int len)
381 {
382   maybeseek(export[a/hunksize], a%hunksize);
383   return (read(export[a/hunksize], buf, len) != len);
384 }
385
386 int expread(off_t a, char *buf, int len)
387 {
388         int rdlen, offset;
389         off_t mapcnt, mapl, maph, pagestart;
390  
391         if (!(flags & F_COPYONWRITE))
392                 return rawexpread(a, buf, len);
393         DEBUG3("Asked to read %d bytes at %Lu.\n", len, (unsigned long long)a);
394
395         mapl=a/DIFFPAGESIZE; maph=(a+len-1)/DIFFPAGESIZE;
396
397         for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
398                 pagestart=mapcnt*DIFFPAGESIZE;
399                 offset=a-pagestart;
400                 rdlen=(len<DIFFPAGESIZE-offset) ? len : DIFFPAGESIZE-offset;
401                 if (difmap[mapcnt]!=(u32)(-1)) { /* the block is already there */
402                         DEBUG3("Page %Lu is at %lu\n", (unsigned long long)mapcnt,
403                                (unsigned long)difmap[mapcnt]);
404                         myseek(difffile, difmap[mapcnt]*DIFFPAGESIZE+offset);
405                         if (read(difffile, buf, rdlen) != rdlen) return -1;
406                 } else { /* the block is not there */
407                         DEBUG2("Page %Lu is not here, we read the original one\n",
408                                (unsigned long long)mapcnt);
409                         return rawexpread(a, buf, rdlen);
410                 }
411                 len-=rdlen; a+=rdlen; buf+=rdlen;
412         }
413         return 0;
414 }
415
416 int rawexpwrite(off_t a, char *buf, int len)
417 {
418         maybeseek(export[a/hunksize], a%hunksize);
419         return (write(export[a/hunksize], buf, len) != len);
420 }
421
422
423 int expwrite(off_t a, char *buf, int len)
424 {
425         u32 mapcnt,mapl,maph ; int wrlen,rdlen ; 
426         off_t pagestart ; int offset ;
427
428         if (!(flags & F_COPYONWRITE))
429                 return(rawexpwrite(a,buf,len)); 
430         DEBUG3("Asked to write %d bytes at %Lu.\n", len, (unsigned long long)a);
431
432         mapl=a/DIFFPAGESIZE ; maph=(a+len-1)/DIFFPAGESIZE ;
433
434         for (mapcnt=mapl;mapcnt<=maph;mapcnt++) {
435                 pagestart=mapcnt*DIFFPAGESIZE ;
436                 offset=a-pagestart ;
437                 wrlen=(len<DIFFPAGESIZE-offset) ? len : DIFFPAGESIZE-offset ;
438
439                 if (difmap[mapcnt]!=(u32)(-1)) { /* the block is already there */
440                         DEBUG3("Page %Lu is at %lu\n", (unsigned long long)mapcnt,
441                                (unsigned long)difmap[mapcnt]) ;
442                         myseek(difffile,difmap[mapcnt]*DIFFPAGESIZE+offset) ;
443                         if (write(difffile, buf, wrlen) != wrlen) return -1 ;
444                 } else { /* the block is not there */
445                         myseek(difffile,difffilelen*DIFFPAGESIZE) ;
446                         difmap[mapcnt]=difffilelen++ ;
447                         DEBUG3("Page %Lu is not here, we put it at %lu\n",
448                                (unsigned long long)mapcnt,
449                                (unsigned long)difmap[mapcnt]);
450                         rdlen=DIFFPAGESIZE ;
451                         if (rdlen+pagestart%hunksize>hunksize) 
452                                 rdlen=hunksize-(pagestart%hunksize) ;
453                         if (rawexpread(pagestart,pagebuf,rdlen)) return -1 ;
454                         memcpy(pagebuf+offset,buf,wrlen) ;
455                         if (write(difffile,pagebuf,DIFFPAGESIZE)!=DIFFPAGESIZE) return -1 ;
456                 }                                                   
457                 len-=wrlen ; a+=wrlen ; buf+=wrlen ;
458         }
459         return 0;
460 }
461
462 int mainloop(int net)
463 {
464         struct nbd_request request;
465         struct nbd_reply reply;
466         char zeros[300];
467         int i = 0;
468         off_t size_host;
469
470         memset(zeros, 0, 290);
471         if (write(net, INIT_PASSWD, 8) < 0)
472                 err("Negotiation failed: %m");
473         cliserv_magic = htonll(cliserv_magic);
474         if (write(net, &cliserv_magic, sizeof(cliserv_magic)) < 0)
475                 err("Negotiation failed: %m");
476         size_host = htonll(exportsize);
477         if (write(net, &size_host, 8) < 0)
478                 err("Negotiation failed: %m");
479         if (write(net, zeros, 128) < 0)
480                 err("Negotiation failed: %m");
481
482         DEBUG("Entering request loop!\n");
483         reply.magic = htonl(NBD_REPLY_MAGIC);
484         reply.error = 0;
485         while (1) {
486 #define BUFSIZE (1024*1024)
487                 char buf[BUFSIZE];
488                 int len;
489 #ifdef DODBG
490                 i++;
491                 printf("%d: ", i);
492 #endif
493
494                 if (timeout) 
495                         alarm(timeout);
496                 readit(net, &request, sizeof(request));
497                 request.from = ntohll(request.from);
498                 request.type = ntohl(request.type);
499
500                 if (request.type==2) { /* Disconnect request */
501                   if (difmap) free(difmap) ;
502                   if (difffile>=0) { 
503                      close(difffile) ; unlink(difffilename) ; }
504                   err("Disconnect request received.") ;
505                 }
506
507                 len = ntohl(request.len);
508
509                 if (request.magic != htonl(NBD_REQUEST_MAGIC))
510                         err("Not enough magic.");
511                 if (len > BUFSIZE)
512                         err("Request too big!");
513 #ifdef DODBG
514                 printf("%s from %Lu (%Lu) len %d, ", request.type ? "WRITE" :
515                                 "READ", (unsigned long long)request.from,
516                                 (unsigned long long)request.from / 512, len);
517 #endif
518                 memcpy(reply.handle, request.handle, sizeof(reply.handle));
519                 if ((request.from + len) > (OFFT_MAX)) {
520                   DEBUG("[Number too large!]");
521                   ERROR;
522                   continue;
523                 }
524                 if ((((off_t)request.from + len) > exportsize) ||
525                     ((flags & F_READONLY) && request.type)) {
526                         DEBUG("[RANGE!]");
527                         ERROR;
528                         continue;
529                 }
530                 if (request.type==1) {  /* WRITE */
531                         DEBUG("wr: net->buf, ");
532                         readit(net, buf, len);
533                         DEBUG("buf->exp, ");
534                         if ((autoreadonly == 1) || expwrite(request.from, buf, len)) {
535                                 DEBUG("Write failed: %m" );
536                                 ERROR;
537                                 continue;
538                         }
539                         lastpoint += len;
540                         SEND;
541                         DEBUG("OK!\n");
542                         continue;
543                 }
544                 /* READ */
545
546                 DEBUG("exp->buf, ");
547                 if (expread(request.from, buf + sizeof(struct nbd_reply), len)) {
548                         lastpoint = -1;
549                         DEBUG("Read failed: %m");
550                         ERROR;
551                         continue;
552                 }
553                 lastpoint += len;
554
555                 DEBUG("buf->net, ");
556                 memcpy(buf, &reply, sizeof(struct nbd_reply));
557                 writeit(net, buf, len + sizeof(struct nbd_reply));
558                 DEBUG("OK!\n");
559         }
560 }
561
562 char exportname2[1024];
563
564 void set_peername(int net,char *clientname)
565 {
566         struct sockaddr_in addrin;
567         int addrinlen = sizeof( addrin );
568         char *peername ;
569
570         if (getpeername( net, (struct sockaddr *) &addrin, &addrinlen ) < 0)
571                 err("getsockname failed: %m");
572         peername = inet_ntoa(addrin.sin_addr);
573         sprintf(exportname2, exportname, peername);
574
575         msg4(LOG_INFO, "connect from %s, assigned file is %s", peername, exportname2);
576         strncpy(clientname,peername,255) ;
577 }
578
579 off_t size_autodetect(int export)
580 {
581         off_t es;
582         u32 es32;
583         struct stat stat_buf;
584         int error;
585
586         DEBUG("looking for export size with lseek SEEK_END\n");
587         es = lseek(export, (off_t)0, SEEK_END);
588         if (es > ((off_t)0)) {
589                 return es;
590         } else {
591                 DEBUG2("lseek failed: %d", errno==EBADF?1:(errno==ESPIPE?2:(errno==EINVAL?3:4)));
592         }
593
594         DEBUG("looking for export size with fstat\n");
595         stat_buf.st_size = 0;
596         error = fstat(export, &stat_buf);
597         if (!error && stat_buf.st_size > 0) {
598                 return (off_t)stat_buf.st_size;
599         } else {
600                 err("fstat failed: %m");
601         }
602
603 #ifdef BLKGETSIZE
604         DEBUG("looking for export size with ioctl BLKGETSIZE\n");
605         if (!ioctl(export, BLKGETSIZE, &es32) && es32) {
606                 es = (off_t)es32 * (off_t)512;
607                 return es;
608         }
609 #endif
610         err("Could not find size of exported block device: %m");
611         return (off_t)-1;
612 }
613
614 int main(int argc, char *argv[])
615 {
616         int net;
617         off_t i;
618
619         if (sizeof( struct nbd_request )!=28) {
620                 fprintf(stderr,"Bad size of structure. Alignment problems?\n");
621                 exit(-1) ;
622         }
623         logging();
624         cmdline(argc, argv);
625         
626         if (!port) return 1 ;
627         connectme(port); /* serve infinitely */
628         return 0 ;
629 }
630
631
632 void serveconnection(int net) 
633 {   
634   off_t i ;
635
636   for (i=0; i<exportsize; i+=hunksize) {
637     char exportname3[1024];
638     
639     sprintf(exportname3, exportname2, i/hunksize);
640     printf( "Opening %s\n", exportname3 );
641     if ((export[i/hunksize] = open(exportname3, (flags & F_READONLY) ? O_RDONLY : O_RDWR)) == -1) {
642                 /* Read WRITE ACCESS was requested by media is only read only */
643                 autoreadonly = 1;
644                 flags |= F_READONLY;
645                 if ((export[i/hunksize] = open(exportname3, O_RDONLY)) == -1) 
646                         err("Could not open exported file: %m");
647         }
648     }
649
650     if (exportsize == (off_t)OFFT_MAX) {
651         exportsize = size_autodetect(export[0]);
652     }
653     if (exportsize > (off_t)OFFT_MAX) {
654         err("Size of exported file is too big\n");
655     }
656     else
657         msg3(LOG_INFO, "size of exported file/device is %Lu",
658                         (unsigned long long)exportsize);
659
660     if (flags & F_COPYONWRITE) {
661       sprintf(difffilename,"%s-%s-%d.diff",exportname2,clientname,
662               (int)getpid()) ;
663       msg3(LOG_INFO,"About to create map and diff file %s",difffilename) ;
664       difffile=open(difffilename,O_RDWR | O_CREAT | O_TRUNC,0600) ;
665       if (difffile<0) err("Could not create diff file (%m)") ;
666       if ((difmap=calloc(exportsize/DIFFPAGESIZE,sizeof(u32)))==NULL)
667           err("Could not allocate memory") ;
668       for (i=0;i<exportsize/DIFFPAGESIZE;i++) difmap[i]=(u32)-1 ;         
669     }
670     
671     setmysockopt(net);
672       
673     mainloop(net);
674 }