r7: Added gzipped nbd code from marc <marc@jade.cs.uct.ac.za>.
[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 <unistd.h>
42
43 #include <sys/ioctl.h>
44 #include <sys/types.h>
45 #include <sys/socket.h>
46
47 #include <netinet/in.h>
48
49 /* asm/types defines __u??, at least on my system */
50 #include <asm/types.h>
51
52 #define u32 __u32
53 #define u64 __u64
54
55 #include <linux/nbd.h>
56
57 #define BLOCK 1024
58
59 /* don't ask me why this value, I only copied it */
60 #define CHUNK BLOCK*20
61
62 /* taken litterally from the original nbd */
63 #ifdef WORDS_BIGENDIAN
64 u64 ntohll(u64 a)
65 {
66   return a;
67 }
68 #else
69 u64 ntohll(u64 a)
70 {
71   u32 lo = a & 0xffffffff;
72   u32 hi = a >> 32U;
73   lo = ntohl(lo);
74   hi = ntohl(hi);
75   return ((u64) lo) << 32U | hi;
76 }
77 #endif
78
79
80 int main(int argc, char **argv)
81 {
82   int pr[2];
83   int sk;
84   int nbd;
85   gzFile *gz;
86   int gzerr;
87
88   char chunk[CHUNK];
89   struct nbd_request request;
90   struct nbd_reply reply;
91
92   u64 size;
93   u64 from;
94   u32 len;
95
96   if(argc<3){
97     printf("Usage: %s nbdevice gzfile [size]\n",argv[0]);
98     exit(1);
99   }
100
101   gz=gzopen(argv[2], "rb");
102   if(gz==NULL){
103     fprintf(stderr,"%s: unable open compressed file %s\n",argv[0],argv[2]);
104     exit(1);
105   }
106
107   if(argc>3){
108     size=atol(argv[3]);
109     if((size==0)||(size%BLOCK)){
110       fprintf(stderr,"%s: %s does not appear to be a valid size\n",argv[0],argv[3]);
111       exit(1);
112     }
113     printf("%s: file=%s, size=%Ld\n",argv[0],argv[2],size);
114   } else {
115     char buffer[BLOCK];
116     int result;
117
118     size=0;
119     printf("%s: file=%s, seeking, ",argv[0],argv[2]);
120     fflush(stdout);
121
122     /* expensive seek to get file size */
123     while(BLOCK==(result=gzread(gz,buffer,BLOCK))){
124       size+=BLOCK;
125     }
126
127     if(result==0){
128       printf("size=%Ld\n",size);
129     } else {
130       printf("failed\n");
131       if(result<0){
132         fprintf(stderr,"%s: read failed: %s\n",argv[0],gzerror(gz,&gzerr));
133       } else {
134         fprintf(stderr,"%s: incomplete last read, file has to be a multiple of %d\n",argv[0],BLOCK);
135       }
136       exit(1);
137     }
138
139     if(gzrewind(gz)!=0){
140       fprintf(stderr,"%s: unable to rewind gzfile\n",argv[0]);
141       exit(1);
142     }
143
144   }
145
146   if(socketpair(AF_UNIX, SOCK_STREAM, 0, pr)){
147     fprintf(stderr,"%s: unable to create socketpair: %s\n",argv[0],strerror(errno));
148     exit(1);
149   }
150
151   switch(fork()){
152     case -1 :
153       fprintf(stderr,"%s: unable to fork: %s\n",argv[0],strerror(errno));
154       exit(1);
155       break;
156     case 0 : /* child */
157       gzclose(gz);
158
159       close(pr[0]);
160
161       sk=pr[1];
162
163       nbd=open(argv[1], O_RDWR);
164       if(nbd<0){
165         fprintf(stderr,"%s: unable to open %s: %s\n",argv[0],argv[1],strerror(errno));
166         exit(1);
167       }
168
169       if(ioctl(nbd,NBD_SET_SIZE,size)<0){
170         fprintf(stderr,"%s: failed to set size for %s: %s\n",argv[0],argv[1],strerror(errno));
171         exit(1);
172       }
173
174       ioctl(nbd, NBD_CLEAR_SOCK);
175
176       if(ioctl(nbd,NBD_SET_SOCK,sk)<0){
177         fprintf(stderr,"%s: failed to set socket for %s: %s\n",argv[0],argv[1],strerror(errno));
178         exit(1);
179       }
180
181       if(ioctl(nbd,NBD_DO_IT)<0){
182         fprintf(stderr,"%s: block device %s terminated: %s\n",argv[0],argv[1],strerror(errno));
183       }
184
185       ioctl(nbd, NBD_CLEAR_QUE);
186       ioctl(nbd, NBD_CLEAR_SOCK);
187
188       exit(0);
189       
190       break;
191   }
192
193   /* only parent here, child always exits */
194
195   close(pr[1]);
196   sk=pr[0];
197
198   reply.magic=htonl(NBD_REPLY_MAGIC);
199   reply.error=htonl(0);
200
201   while(1){
202
203     if(read(sk,&request,sizeof(request))!=sizeof(request)){
204       fprintf(stderr,"%s: incomplete request\n",argv[0]);
205     }
206
207     memcpy(reply.handle,request.handle,sizeof(reply.handle));
208
209     len=ntohl(request.len);
210     from=ntohll(request.from);
211
212 #ifdef TRACE
213 fprintf(stderr,"%s: len=%d, from=%Ld\n",argv[0],len,from);
214 #endif
215
216     if(request.magic!=htonl(NBD_REQUEST_MAGIC)){
217       fprintf(stderr,"%s: bad magic\n",argv[0]);
218       reply.error=htonl(EIO); /* is that the right way of doing things ? */
219     }
220
221     if(ntohl(request.type)){
222       fprintf(stderr,"%s: unsupported write request\n",argv[0]);
223       reply.error=htonl(EROFS);
224     }
225
226     if(len+sizeof(struct nbd_reply)>CHUNK){
227       fprintf(stderr,"%s: request too long\n",argv[0]);
228       reply.error=htonl(EIO);
229     }
230
231     if(len+from>size){
232       fprintf(stderr,"%s: request outside range\n",argv[0]);
233       reply.error=htonl(EIO);
234     }
235
236     if(reply.error==htonl(0)){
237       gzseek(gz,from,0);
238       if(gzread(gz,chunk+sizeof(struct nbd_reply),len)!=len){
239         fprintf(stderr,"%s: unable to read\n",argv[0]);
240         reply.error=htonl(EIO);
241         len=0;
242       }
243     } else {
244       len=0;
245     }
246
247     memcpy(chunk,&reply,sizeof(struct nbd_reply));
248     if(write(sk,chunk,len+sizeof(struct nbd_reply))!=(len+sizeof(struct nbd_reply))){
249       fprintf(stderr,"%s: write failed: %s\n",argv[0],strerror(errno));
250     }
251   }
252
253   gzclose(gz);
254
255   return 0;
256 }