Ensure this is available, too
[nbd.git] / gznbd / gznbd.c
blob83eb63e4e1c7eb5996e50e205e377439bf7476b9
1 /*
2 (c) Marc Welz 2000, released under GPL, tested under Linux 2.2.17
4 Most of the stuff cribbed from the nbd package written by Pavel Machek
6 Unfortunately quite slow since zlib has to decompress all the stuff between
7 seeks, so only suited to smaller files
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
12 Usage
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
23 gznbd does not background, from another terminal type
25 mount -o ro,nocheck /dev/nbd0 /mnt/
26 cd /mnt
30 ro is important, since writes will fail horribly and nochecks
31 speeds the mount up nicely
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 #include <inttypes.h>
45 #include <sys/ioctl.h>
46 #include <sys/types.h>
47 #include <sys/socket.h>
49 #include <netinet/in.h>
51 /* asm/types defines __u??, at least on my system */
52 #include <asm/types.h>
54 #define MY_NAME "gznbd"
56 /* these headers take care of endianness */
57 #include "../config.h"
58 #include "../cliserv.h"
60 #define BLOCK 1024
62 /* don't ask me why this value, I only copied it */
63 #define CHUNK BLOCK*20
66 int main(int argc, char **argv)
68 int pr[2];
69 int sk;
70 int nbd;
71 gzFile gz;
72 int gzerr;
74 char chunk[CHUNK];
75 struct nbd_request request;
76 struct nbd_reply reply;
78 u64 size;
79 u64 from;
80 u32 len;
82 if(argc<3){
83 printf("Usage: %s nbdevice gzfile [size]\n",argv[0]);
84 exit(1);
87 gz=gzopen(argv[2], "rb");
88 if(gz==NULL){
89 fprintf(stderr,"%s: unable open compressed file %s\n",argv[0],argv[2]);
90 exit(1);
93 if(argc>3){
94 size=atoll(argv[3]);
95 if((size==0)||(size%BLOCK)){
96 fprintf(stderr,"%s: %s does not appear to be a valid size\n",argv[0],argv[3]);
97 exit(1);
99 printf("%s: file=%s, size=%"PRId64"\n",argv[0],argv[2],size);
100 } else {
101 char buffer[BLOCK];
102 int result;
104 size=0;
105 printf("%s: file=%s, seeking, ",argv[0],argv[2]);
106 fflush(stdout);
108 /* expensive seek to get file size */
109 while(BLOCK==(result=gzread(gz,buffer,BLOCK))){
110 size+=BLOCK;
113 if(result==0){
114 printf("size=%"PRId64"\n",size);
115 } else {
116 printf("failed\n");
117 if(result<0){
118 fprintf(stderr,"%s: read failed: %s\n",argv[0],gzerror(gz,&gzerr));
119 } else {
120 fprintf(stderr,"%s: incomplete last read, file has to be a multiple of %d\n",argv[0],BLOCK);
122 exit(1);
125 if(gzrewind(gz)!=0){
126 fprintf(stderr,"%s: unable to rewind gzfile\n",argv[0]);
127 exit(1);
132 if(socketpair(AF_UNIX, SOCK_STREAM, 0, pr)){
133 fprintf(stderr,"%s: unable to create socketpair: %s\n",argv[0],strerror(errno));
134 exit(1);
137 switch(fork()){
138 case -1 :
139 fprintf(stderr,"%s: unable to fork: %s\n",argv[0],strerror(errno));
140 exit(1);
141 break;
142 case 0 : /* child */
143 gzclose(gz);
145 close(pr[0]);
147 sk=pr[1];
149 nbd=open(argv[1], O_RDWR);
150 if(nbd<0){
151 fprintf(stderr,"%s: unable to open %s: %s\n",argv[0],argv[1],strerror(errno));
152 exit(1);
155 if(ioctl(nbd,NBD_SET_SIZE,size)<0){
156 fprintf(stderr,"%s: failed to set size for %s: %s\n",argv[0],argv[1],strerror(errno));
157 exit(1);
160 ioctl(nbd, NBD_CLEAR_SOCK);
162 if(ioctl(nbd,NBD_SET_SOCK,sk)<0){
163 fprintf(stderr,"%s: failed to set socket for %s: %s\n",argv[0],argv[1],strerror(errno));
164 exit(1);
167 if(ioctl(nbd,NBD_DO_IT)<0){
168 fprintf(stderr,"%s: block device %s terminated: %s\n",argv[0],argv[1],strerror(errno));
171 ioctl(nbd, NBD_CLEAR_QUE);
172 ioctl(nbd, NBD_CLEAR_SOCK);
174 exit(0);
176 break;
179 /* only parent here, child always exits */
181 close(pr[1]);
182 sk=pr[0];
184 reply.magic=htonl(NBD_REPLY_MAGIC);
185 reply.error=htonl(0);
187 while(1){
189 if(read(sk,&request,sizeof(request))!=sizeof(request)){
190 fprintf(stderr,"%s: incomplete request\n",argv[0]);
193 memcpy(reply.handle,request.handle,sizeof(reply.handle));
195 len=ntohl(request.len);
196 from=ntohll(request.from);
198 #ifdef TRACE
199 fprintf(stderr,"%s: len=%d, from=%"PRId64"\n",argv[0],len,from);
200 #endif
202 if(request.magic!=htonl(NBD_REQUEST_MAGIC)){
203 fprintf(stderr,"%s: bad magic\n",argv[0]);
204 reply.error=htonl(EIO); /* is that the right way of doing things ? */
207 if(ntohl(request.type)){
208 fprintf(stderr,"%s: unsupported write request\n",argv[0]);
209 reply.error=htonl(EROFS);
212 if(len+sizeof(struct nbd_reply)>CHUNK){
213 fprintf(stderr,"%s: request too long\n",argv[0]);
214 reply.error=htonl(EIO);
217 if(len+from>size){
218 fprintf(stderr,"%s: request outside range\n",argv[0]);
219 reply.error=htonl(EIO);
222 if(reply.error==htonl(0)){
223 gzseek(gz,from,0);
224 if(gzread(gz,chunk+sizeof(struct nbd_reply),len)!=len){
225 fprintf(stderr,"%s: unable to read\n",argv[0]);
226 reply.error=htonl(EIO);
227 len=0;
229 } else {
230 len=0;
233 memcpy(chunk,&reply,sizeof(struct nbd_reply));
234 if(write(sk,chunk,len+sizeof(struct nbd_reply))!=(len+sizeof(struct nbd_reply))){
235 fprintf(stderr,"%s: write failed: %s\n",argv[0],strerror(errno));
239 gzclose(gz);
241 return 0;