diff options
-rw-r--r-- | networking/nbd-client.c | 230 |
1 files changed, 186 insertions, 44 deletions
diff --git a/networking/nbd-client.c b/networking/nbd-client.c index bedb01a1c..103756b59 100644 --- a/networking/nbd-client.c +++ b/networking/nbd-client.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include "libbb.h" | 16 | #include "libbb.h" |
17 | #include <netinet/tcp.h> | 17 | #include <netinet/tcp.h> |
18 | #include <linux/fs.h> | 18 | #include <linux/fs.h> |
19 | #include <getopt.h> | ||
19 | 20 | ||
20 | #define NBD_SET_SOCK _IO(0xab, 0) | 21 | #define NBD_SET_SOCK _IO(0xab, 0) |
21 | #define NBD_SET_BLKSIZE _IO(0xab, 1) | 22 | #define NBD_SET_BLKSIZE _IO(0xab, 1) |
@@ -27,57 +28,144 @@ | |||
27 | #define NBD_SET_SIZE_BLOCKS _IO(0xab, 7) | 28 | #define NBD_SET_SIZE_BLOCKS _IO(0xab, 7) |
28 | #define NBD_DISCONNECT _IO(0xab, 8) | 29 | #define NBD_DISCONNECT _IO(0xab, 8) |
29 | #define NBD_SET_TIMEOUT _IO(0xab, 9) | 30 | #define NBD_SET_TIMEOUT _IO(0xab, 9) |
31 | #define NBD_SET_FLAGS _IO(0xab, 10) | ||
30 | 32 | ||
31 | //usage:#define nbdclient_trivial_usage | 33 | //usage:#define nbdclient_trivial_usage |
32 | //usage: "HOST PORT BLOCKDEV" | 34 | //usage: "{ [-b BLKSIZE] [-N NAME] [-t SEC] [-p] HOST [PORT] | -d } BLOCKDEV" |
33 | //usage:#define nbdclient_full_usage "\n\n" | 35 | //usage:#define nbdclient_full_usage "\n\n" |
34 | //usage: "Connect to HOST and provide a network block device on BLOCKDEV" | 36 | //usage: "Connect to HOST and provide network block device on BLOCKDEV" |
35 | 37 | ||
36 | //TODO: more compat with nbd-client version 2.9.13 - | 38 | //TODO: more compat with nbd-client version 3.17 - |
37 | //Usage: nbd-client [bs=blocksize] [timeout=sec] host port nbd_device [-swap] [-persist] [-nofork] | 39 | //nbd-client host [ port ] nbd-device [ -connections num ] [ -sdp ] [ -swap ] |
38 | //Or : nbd-client -d nbd_device | 40 | // [ -persist ] [ -nofork ] [ -nonetlink ] [ -systemd-mark ] |
39 | //Or : nbd-client -c nbd_device | 41 | // [ -block-size block size ] [ -timeout seconds ] [ -name name ] |
40 | //Default value for blocksize is 1024 (recommended for ethernet) | 42 | // [ -certfile certfile ] [ -keyfile keyfile ] [ -cacertfile cacertfile ] |
43 | // [ -tlshostname hostname ] | ||
44 | //nbd-client -unix path nbd-device [ -connections num ] [ -sdp ] [ -swap ] | ||
45 | // [ -persist ] [ -nofork ] [ -nonetlink ] [ -systemd-mark ] | ||
46 | // [ -block-size block size ] [ -timeout seconds ] [ -name name ] | ||
47 | //nbd-client nbd-device | ||
48 | //nbd-client -d nbd-device | ||
49 | //nbd-client -c nbd-device | ||
50 | //nbd-client -l host [ port ] | ||
51 | //nbd-client [ -netlink ] -l host | ||
52 | // | ||
53 | //Default value for blocksize is 4096 | ||
41 | //Allowed values for blocksize are 512,1024,2048,4096 | 54 | //Allowed values for blocksize are 512,1024,2048,4096 |
42 | //Note, that kernel 2.4.2 and older ones do not work correctly with | ||
43 | //blocksizes other than 1024 without patches | ||
44 | 55 | ||
45 | int nbdclient_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 56 | int nbdclient_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
46 | int nbdclient_main(int argc UNUSED_PARAM, char **argv) | 57 | int nbdclient_main(int argc, char **argv) |
47 | { | 58 | { |
48 | unsigned long timeout = 0; | ||
49 | #if BB_MMU | 59 | #if BB_MMU |
50 | int nofork = 0; | 60 | bool nofork; |
51 | #endif | 61 | #endif |
52 | char *host, *port, *device; | 62 | bool opt_d; |
63 | bool opt_p; | ||
64 | const char *host, *port, *device; | ||
65 | const char *name; | ||
66 | unsigned blksize, size_blocks; | ||
67 | unsigned timeout; | ||
68 | int ch; | ||
53 | struct nbd_header_t { | 69 | struct nbd_header_t { |
54 | uint64_t magic1; // "NBDMAGIC" | 70 | uint64_t magic1; // "NBDMAGIC" |
55 | uint64_t magic2; // 0x420281861253 big endian | 71 | uint64_t magic2; // old style: 0x420281861253 big endian |
72 | // // new style: 0x49484156454F5054 (IHAVEOPT) | ||
73 | } nbd_header; | ||
74 | struct old_nbd_header_t { | ||
56 | uint64_t devsize; | 75 | uint64_t devsize; |
57 | uint32_t flags; | 76 | uint32_t flags; |
58 | char data[124]; | 77 | char data[124]; |
59 | } nbd_header; | 78 | } old_nbd_header; |
79 | struct new_nbd_header_t { | ||
80 | uint64_t devsize; | ||
81 | uint16_t transmission_flags; | ||
82 | char data[124]; | ||
83 | } new_nbd_header; | ||
84 | struct nbd_opt_t { | ||
85 | uint64_t magic; | ||
86 | uint32_t opt; | ||
87 | uint32_t len; | ||
88 | } nbd_opts; | ||
60 | 89 | ||
61 | BUILD_BUG_ON(offsetof(struct nbd_header_t, data) != 8+8+8+4); | 90 | static const struct option long_options[] = { |
91 | { "block-size", required_argument, NULL, 'b' }, | ||
92 | { "timeout" , required_argument, NULL, 't' }, | ||
93 | { "name" , required_argument, NULL, 'n' }, | ||
94 | { "persist" , no_argument , NULL, 'p' }, | ||
95 | { NULL } | ||
96 | }; | ||
62 | 97 | ||
63 | // Parse command line stuff (just a stub now) | 98 | BUILD_BUG_ON(offsetof(struct old_nbd_header_t, data) != 8 + 4); |
64 | if (!argv[1] || !argv[2] || !argv[3] || argv[4]) | 99 | BUILD_BUG_ON(offsetof(struct new_nbd_header_t, data) != 8 + 2); |
65 | bb_show_usage(); | ||
66 | 100 | ||
67 | #if !BB_MMU | 101 | #if !BB_MMU |
68 | bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); | 102 | bb_daemonize_or_rexec(DAEMON_CLOSE_EXTRA_FDS, argv); |
69 | #endif | 103 | #endif |
70 | 104 | ||
71 | host = argv[1]; | 105 | // Parse args. nbd-client uses stupid "one-dash long options" style :( |
72 | port = argv[2]; | 106 | // Even though short forms (-b,-t,-N,-p) exist for all long opts, |
73 | device = argv[3]; | 107 | // older manpages only contained long forms, which probably resulted |
108 | // in many scripts using them. | ||
109 | blksize = 4096; | ||
110 | timeout = 0; | ||
111 | name = ""; // use of "" instead of NULL simplifies strlen() later | ||
112 | opt_d = opt_p = 0; | ||
113 | while ((ch = getopt_long_only(argc, argv, "dN:", long_options, NULL)) != -1) { | ||
114 | switch (ch) { | ||
115 | case 'p': // -persist | ||
116 | opt_p = 1; | ||
117 | break; | ||
118 | case 'd': // -d | ||
119 | opt_d = 1; | ||
120 | break; | ||
121 | case 'b': // -block-size | ||
122 | blksize = xatou(optarg); | ||
123 | break; | ||
124 | case 't': // -timeout | ||
125 | timeout = xatou(optarg); | ||
126 | break; | ||
127 | case 'N': // -N | ||
128 | case 'n': // -name | ||
129 | name = optarg; | ||
130 | break; | ||
131 | default: | ||
132 | bb_show_usage(); | ||
133 | } | ||
134 | } | ||
135 | argv += optind; | ||
136 | |||
137 | if (opt_d) { // -d | ||
138 | if (argv[0] && !argv[1]) { | ||
139 | int nbd = xopen(argv[0], O_RDWR); | ||
140 | ioctl(nbd, NBD_DISCONNECT); | ||
141 | ioctl(nbd, NBD_CLEAR_SOCK); | ||
142 | if (ENABLE_FEATURE_CLEAN_UP) | ||
143 | close(nbd); | ||
144 | return 0; | ||
145 | } | ||
146 | bb_show_usage(); | ||
147 | } | ||
148 | |||
149 | // Allow only argv[] of: HOST [PORT] BLOCKDEV | ||
150 | if (!argv[0] || !argv[1] || (argv[2] && argv[3])) { | ||
151 | bb_show_usage(); | ||
152 | } | ||
74 | 153 | ||
75 | // Repeat until spanked (-persist behavior) | 154 | host = argv[0]; |
76 | for (;;) { | 155 | port = argv[2] ? argv[1] : "10809"; |
156 | device = argv[2] ? argv[2] : argv[1]; | ||
157 | |||
158 | // Repeat until spanked if -persist | ||
159 | #if BB_MMU | ||
160 | nofork = 0; | ||
161 | #endif | ||
162 | do { | ||
77 | int sock, nbd; | 163 | int sock, nbd; |
78 | int ro; | 164 | int ro; |
165 | int proto_new; // 0 for old, 1 for new | ||
166 | char *data; | ||
79 | 167 | ||
80 | // Make sure the /dev/nbd exists | 168 | // Make sure BLOCKDEV exists |
81 | nbd = xopen(device, O_RDWR); | 169 | nbd = xopen(device, O_RDWR); |
82 | 170 | ||
83 | // Find and connect to server | 171 | // Find and connect to server |
@@ -85,40 +173,95 @@ int nbdclient_main(int argc UNUSED_PARAM, char **argv) | |||
85 | setsockopt_1(sock, IPPROTO_TCP, TCP_NODELAY); | 173 | setsockopt_1(sock, IPPROTO_TCP, TCP_NODELAY); |
86 | 174 | ||
87 | // Log on to the server | 175 | // Log on to the server |
88 | xread(sock, &nbd_header, 8+8+8+4 + 124); | 176 | xread(sock, &nbd_header, 8 + 8); |
89 | if (memcmp(&nbd_header.magic1, "NBDMAGIC""\x00\x00\x42\x02\x81\x86\x12\x53", 16) != 0) | 177 | if (memcmp(&nbd_header.magic1, "NBDMAGIC", |
178 | sizeof(nbd_header.magic1)) != 0 | ||
179 | ) { | ||
180 | bb_error_msg_and_die("login failed"); | ||
181 | } | ||
182 | if (memcmp(&nbd_header.magic2, | ||
183 | "\x00\x00\x42\x02\x81\x86\x12\x53", | ||
184 | sizeof(nbd_header.magic2)) == 0 | ||
185 | ) { | ||
186 | proto_new = 0; | ||
187 | } else if (memcmp(&nbd_header.magic2, "IHAVEOPT", 8) == 0) { | ||
188 | proto_new = 1; | ||
189 | } else { | ||
90 | bb_error_msg_and_die("login failed"); | 190 | bb_error_msg_and_die("login failed"); |
191 | } | ||
192 | |||
193 | if (!proto_new) { | ||
194 | xread(sock, &old_nbd_header, | ||
195 | sizeof(old_nbd_header.devsize) + | ||
196 | sizeof(old_nbd_header.flags) + | ||
197 | sizeof(old_nbd_header.data)); | ||
198 | size_blocks = SWAP_BE64(old_nbd_header.devsize) / blksize; | ||
199 | ioctl(nbd, NBD_SET_BLKSIZE, (unsigned long) blksize); | ||
200 | ioctl(nbd, NBD_SET_SIZE_BLOCKS, size_blocks); | ||
201 | ioctl(nbd, NBD_CLEAR_SOCK); | ||
202 | ro = !!(old_nbd_header.flags & htons(2)); | ||
203 | data = old_nbd_header.data; | ||
204 | } else { | ||
205 | unsigned namelen; | ||
206 | uint16_t handshake_flags; | ||
207 | |||
208 | xread(sock, &handshake_flags, sizeof(handshake_flags)); | ||
209 | xwrite(sock, &const_int_0, sizeof(const_int_0)); // client_flags | ||
210 | |||
211 | memcpy(&nbd_opts.magic, "IHAVEOPT", | ||
212 | sizeof(nbd_opts.magic)); | ||
213 | nbd_opts.opt = htonl(1); // NBD_OPT_EXPORT_NAME | ||
214 | namelen = strlen(name); | ||
215 | nbd_opts.len = htonl(namelen); | ||
216 | xwrite(sock, &nbd_opts, | ||
217 | sizeof(nbd_opts.magic) + | ||
218 | sizeof(nbd_opts.opt) + | ||
219 | sizeof(nbd_opts.len)); | ||
220 | xwrite(sock, name, namelen); | ||
91 | 221 | ||
92 | // Set 4k block size. Everything uses that these days | 222 | xread(sock, &new_nbd_header, |
93 | ioctl(nbd, NBD_SET_BLKSIZE, 4096); | 223 | sizeof(new_nbd_header.devsize) + |
94 | ioctl(nbd, NBD_SET_SIZE_BLOCKS, SWAP_BE64(nbd_header.devsize) / 4096); | 224 | sizeof(new_nbd_header.transmission_flags) + |
95 | ioctl(nbd, NBD_CLEAR_SOCK); | 225 | sizeof(new_nbd_header.data)); |
226 | size_blocks = SWAP_BE64(new_nbd_header.devsize) / blksize; | ||
227 | ioctl(nbd, NBD_SET_BLKSIZE, (unsigned long) blksize); | ||
228 | ioctl(nbd, NBD_SET_SIZE_BLOCKS, size_blocks); | ||
229 | ioctl(nbd, NBD_CLEAR_SOCK); | ||
230 | ioctl(nbd, NBD_SET_FLAGS, | ||
231 | ntohs(new_nbd_header.transmission_flags)); | ||
232 | ro = !!(new_nbd_header.transmission_flags & htons(2)); | ||
233 | data = new_nbd_header.data; | ||
234 | } | ||
96 | 235 | ||
97 | // If the sucker was exported read only, respect that locally | 236 | if (ioctl(nbd, BLKROSET, &ro) < 0) { |
98 | ro = (nbd_header.flags & SWAP_BE32(2)) / SWAP_BE32(2); | ||
99 | if (ioctl(nbd, BLKROSET, &ro) < 0) | ||
100 | bb_perror_msg_and_die("BLKROSET"); | 237 | bb_perror_msg_and_die("BLKROSET"); |
238 | } | ||
101 | 239 | ||
102 | if (timeout) | 240 | if (timeout) { |
103 | if (ioctl(nbd, NBD_SET_TIMEOUT, timeout)) | 241 | if (ioctl(nbd, NBD_SET_TIMEOUT, (unsigned long) timeout)) { |
104 | bb_perror_msg_and_die("NBD_SET_TIMEOUT"); | 242 | bb_perror_msg_and_die("NBD_SET_TIMEOUT"); |
105 | if (ioctl(nbd, NBD_SET_SOCK, sock)) | 243 | } |
106 | bb_perror_msg_and_die("NBD_SET_SOCK"); | 244 | } |
107 | 245 | ||
108 | // if (swap) mlockall(MCL_CURRENT|MCL_FUTURE); | 246 | if (ioctl(nbd, NBD_SET_SOCK, sock)) { |
247 | bb_perror_msg_and_die("NBD_SET_SOCK"); | ||
248 | } | ||
109 | 249 | ||
250 | //if (swap) mlockall(MCL_CURRENT|MCL_FUTURE); | ||
110 | #if BB_MMU | 251 | #if BB_MMU |
111 | // Open the device to force reread of the partition table. | 252 | // Open the device to force reread of the partition table. |
112 | // Need to do it in a separate process, since open(device) | 253 | // Need to do it in a separate process, since open(device) |
113 | // needs some other process to sit in ioctl(nbd, NBD_DO_IT). | 254 | // needs some other process to sit in ioctl(nbd, NBD_DO_IT). |
114 | if (fork() == 0) { | 255 | if (fork() == 0) { |
256 | /* child */ | ||
115 | char *s = strrchr(device, '/'); | 257 | char *s = strrchr(device, '/'); |
116 | sprintf(nbd_header.data, "/sys/block/%.32s/pid", s ? s + 1 : device); | 258 | sprintf(data, "/sys/block/%.32s/pid", s ? s + 1 : device); |
117 | // Is it up yet? | 259 | // Is it up yet? |
118 | for (;;) { | 260 | for (;;) { |
119 | int fd = open(nbd_header.data, O_RDONLY); | 261 | int fd = open(data, O_RDONLY); |
120 | if (fd >= 0) { | 262 | if (fd >= 0) { |
121 | //close(fd); | 263 | if (ENABLE_FEATURE_CLEAN_UP) |
264 | close(fd); | ||
122 | break; | 265 | break; |
123 | } | 266 | } |
124 | sleep(1); | 267 | sleep(1); |
@@ -133,7 +276,6 @@ int nbdclient_main(int argc UNUSED_PARAM, char **argv) | |||
133 | nofork = 1; | 276 | nofork = 1; |
134 | } | 277 | } |
135 | #endif | 278 | #endif |
136 | |||
137 | // This turns us (the process that calls this ioctl) | 279 | // This turns us (the process that calls this ioctl) |
138 | // into a dedicated NBD request handler. | 280 | // into a dedicated NBD request handler. |
139 | // We block here for a long time. | 281 | // We block here for a long time. |
@@ -148,7 +290,7 @@ int nbdclient_main(int argc UNUSED_PARAM, char **argv) | |||
148 | 290 | ||
149 | close(sock); | 291 | close(sock); |
150 | close(nbd); | 292 | close(nbd); |
151 | } | 293 | } while (opt_p); |
152 | 294 | ||
153 | return 0; | 295 | return 0; |
154 | } | 296 | } |