Merge branch 'alex'
[nbd.git] / gznbd / gznbd.c
1 /* 
2    (c) Marc Welz 2000, released under GPL, tested under Linux 2.2.17
3
4    Most of the stuff cribbed from the nbd package written by Pavel Machek
5
6    Unfortunately quite slow since zlib has to decompress all the stuff between
7    seeks, so only suited to smaller files
8    
9    Could be a neat way to do userland encryption/steganography if you have 
10    a crypto library which has a stdiolike interface to replace zlib
11
12    Usage
13
14      dd if=/dev/zero of=/tmp/image bs=1024 count=1024
15      mke2fs -f /tmp/image
16      mount -o loop /tmp/image /mnt/
17      cp /bin/ls /mnt/
18      umount /mnt
19      sync
20      gzip -9 /tmp/image
21      gznbd /dev/nbd0 /tmp/image.gz
22
23    gznbd does not background, from another terminal type
24
25      mount -o ro,nocheck /dev/nbd0 /mnt/
26      cd /mnt
27      ls
28      df
29
30    ro is important, since writes will fail horribly and nochecks
31    speeds the mount up nicely
32
33  */
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <zlib.h>
38 #include <errno.h>
39 #include <string.h>
40 #include <fcntl.h>
41 #include <syslog.h>
42 #include <unistd.h>
43
44 #include <sys/ioctl.h>
45 #include <sys/types.h>
46 #include <sys/socket.h>
47
48 #include <netinet/in.h>
49
50 /* asm/types defines __u??, at least on my system */
51 #include <asm/types.h>
52
53 #define MY_NAME "gznbd"
54
55 /* these headers take care of endianness */
56 #include "../config.h"
57 #include "../cliserv.h"
58
59 #define BLOCK 1024
60
61 /* don't ask me why this value, I only copied it */
62 #define CHUNK BLOCK*20
63
64
65 int main(int argc, char **argv)
66 {
67   int pr[2];
68   int sk;
69   int nbd;
70   gzFile *gz;
71   int gzerr;
72
73   char chunk[CHUNK];
74   struct nbd_request request;
75   struct nbd_reply reply;
76
77   u64 size;
78   u64 from;
79   u32 len;
80
81   if(argc<3){
82     printf("Usage: %s nbdevice gzfile [size]\n",argv[0]);
83     exit(1);
84   }
85
86   gz=gzopen(argv[2], "rb");
87   if(gz==NULL){
88     fprintf(stderr,"%s: unable open compressed file %s\n",argv[0],argv[2]);
89     exit(1);
90   }
91
92   if(argc>3){
93     size=atol(argv[3]);
94     if((size==0)||(size%BLOCK)){
95       fprintf(stderr,"%s: %s does not appear to be a valid size\n",argv[0],argv[3]);
96       exit(1);
97     }
98     printf("%s: file=%s, size=%Ld\n",argv[0],argv[2],size);
99   } else {
100     char buffer[BLOCK];
101     int result;
102
103     size=0;
104     printf("%s: file=%s, seeking, ",argv[0],argv[2]);
105     fflush(stdout);
106
107     /* expensive seek to get file size */
108     while(BLOCK==(result=gzread(gz,buffer,BLOCK))){
109       size+=BLOCK;
110     }
111
112     if(result==0){
113       printf("size=%Ld\n",size);
114     } else {
115       printf("failed\n");
116       if(result<0){
117         fprintf(stderr,"%s: read failed: %s\n",argv[0],gzerror(gz,&gzerr));
118       } else {
119         fprintf(stderr,"%s: incomplete last read, file has to be a multiple of %d\n",argv[0],BLOCK);
120       }
121       exit(1);
122     }
123
124     if(gzrewind(gz)!=0){
125       fprintf(stderr,"%s: unable to rewind gzfile\n",argv[0]);
126       exit(1);
127     }
128
129   }
130
131   if(socketpair(AF_UNIX, SOCK_STREAM, 0, pr)){
132     fprintf(stderr,"%s: unable to create socketpair: %s\n",argv[0],strerror(errno));
133     exit(1);
134   }
135
136   switch(fork()){
137     case -1 :
138       fprintf(stderr,"%s: unable to fork: %s\n",argv[0],strerror(errno));
139       exit(1);
140       break;
141     case 0 : /* child */
142       gzclose(gz);
143
144       close(pr[0]);
145
146       sk=pr[1];
147
148       nbd=open(argv[1], O_RDWR);
149       if(nbd<0){
150         fprintf(stderr,"%s: unable to open %s: %s\n",argv[0],argv[1],strerror(errno));
151         exit(1);
152       }
153
154       if(ioctl(nbd,NBD_SET_SIZE,size)<0){
155         fprintf(stderr,"%s: failed to set size for %s: %s\n",argv[0],argv[1],strerror(errno));
156         exit(1);
157       }
158
159       ioctl(nbd, NBD_CLEAR_SOCK);
160
161       if(ioctl(nbd,NBD_SET_SOCK,sk)<0){
162         fprintf(stderr,"%s: failed to set socket for %s: %s\n",argv[0],argv[1],strerror(errno));
163         exit(1);
164       }
165
166       if(ioctl(nbd,NBD_DO_IT)<0){
167         fprintf(stderr,"%s: block device %s terminated: %s\n",argv[0],argv[1],strerror(errno));
168       }
169
170       ioctl(nbd, NBD_CLEAR_QUE);
171       ioctl(nbd, NBD_CLEAR_SOCK);
172
173       exit(0);
174       
175       break;
176   }
177
178   /* only parent here, child always exits */
179
180   close(pr[1]);
181   sk=pr[0];
182
183   reply.magic=htonl(NBD_REPLY_MAGIC);
184   reply.error=htonl(0);
185
186   while(1){
187
188     if(read(sk,&request,sizeof(request))!=sizeof(request)){
189       fprintf(stderr,"%s: incomplete request\n",argv[0]);
190     }
191
192     memcpy(reply.handle,request.handle,sizeof(reply.handle));
193
194     len=ntohl(request.len);
195     from=ntohll(request.from);
196
197 #ifdef TRACE
198 fprintf(stderr,"%s: len=%d, from=%Ld\n",argv[0],len,from);
199 #endif
200
201     if(request.magic!=htonl(NBD_REQUEST_MAGIC)){
202       fprintf(stderr,"%s: bad magic\n",argv[0]);
203       reply.error=htonl(EIO); /* is that the right way of doing things ? */
204     }
205
206     if(ntohl(request.type)){
207       fprintf(stderr,"%s: unsupported write request\n",argv[0]);
208       reply.error=htonl(EROFS);
209     }
210
211     if(len+sizeof(struct nbd_reply)>CHUNK){
212       fprintf(stderr,"%s: request too long\n",argv[0]);
213       reply.error=htonl(EIO);
214     }
215
216     if(len+from>size){
217       fprintf(stderr,"%s: request outside range\n",argv[0]);
218       reply.error=htonl(EIO);
219     }
220
221     if(reply.error==htonl(0)){
222       gzseek(gz,from,0);
223       if(gzread(gz,chunk+sizeof(struct nbd_reply),len)!=len){
224         fprintf(stderr,"%s: unable to read\n",argv[0]);
225         reply.error=htonl(EIO);
226         len=0;
227       }
228     } else {
229       len=0;
230     }
231
232     memcpy(chunk,&reply,sizeof(struct nbd_reply));
233     if(write(sk,chunk,len+sizeof(struct nbd_reply))!=(len+sizeof(struct nbd_reply))){
234       fprintf(stderr,"%s: write failed: %s\n",argv[0],strerror(errno));
235     }
236   }
237
238   gzclose(gz);
239
240   return 0;
241 }