btrfs-progs: send-stream: don't use single letter variable in tlv_get
[platform/upstream/btrfs-progs.git] / send-stream.c
1 /*
2  * Copyright (C) 2012 Alexander Block.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public
6  * License v2 as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public
14  * License along with this program; if not, write to the
15  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16  * Boston, MA 021110-1307, USA.
17  */
18
19 #include <uuid/uuid.h>
20 #include <unistd.h>
21
22 #include "send.h"
23 #include "send-stream.h"
24 #include "crc32c.h"
25 #include "utils.h"
26
27 struct btrfs_send_stream {
28         int fd;
29         char read_buf[BTRFS_SEND_BUF_SIZE];
30
31         int cmd;
32         struct btrfs_cmd_header *cmd_hdr;
33         struct btrfs_tlv_header *cmd_attrs[BTRFS_SEND_A_MAX + 1];
34         u32 version;
35
36         struct btrfs_send_ops *ops;
37         void *user;
38 };
39
40 static int read_buf(struct btrfs_send_stream *sctx, char *buf, size_t len)
41 {
42         int ret;
43         size_t pos = 0;
44
45         while (pos < len) {
46                 ssize_t rbytes;
47
48                 rbytes = read(sctx->fd, buf + pos, len - pos);
49                 if (rbytes < 0) {
50                         ret = -errno;
51                         error("read from stream failed: %s",
52                                         strerror(-ret));
53                         goto out;
54                 }
55                 if (rbytes == 0) {
56                         ret = 1;
57                         goto out;
58                 }
59                 pos += rbytes;
60         }
61
62         ret = 0;
63
64 out:
65         return ret;
66 }
67
68 /*
69  * Reads a single command from kernel space and decodes the TLV's into
70  * sctx->cmd_attrs
71  */
72 static int read_cmd(struct btrfs_send_stream *sctx)
73 {
74         int ret;
75         int cmd;
76         u32 cmd_len;
77         char *data;
78         u32 pos;
79         u32 crc;
80         u32 crc2;
81
82         memset(sctx->cmd_attrs, 0, sizeof(sctx->cmd_attrs));
83
84         ASSERT(sizeof(*sctx->cmd_hdr) <= sizeof(sctx->read_buf));
85         ret = read_buf(sctx, sctx->read_buf, sizeof(*sctx->cmd_hdr));
86         if (ret < 0)
87                 goto out;
88         if (ret) {
89                 ret = -EINVAL;
90                 error("unexpected EOF in stream");
91                 goto out;
92         }
93
94         sctx->cmd_hdr = (struct btrfs_cmd_header *)sctx->read_buf;
95         cmd = le16_to_cpu(sctx->cmd_hdr->cmd);
96         cmd_len = le32_to_cpu(sctx->cmd_hdr->len);
97
98         if (cmd_len + sizeof(*sctx->cmd_hdr) >= sizeof(sctx->read_buf)) {
99                 ret = -EINVAL;
100                 error("command length %u too big for buffer %zu",
101                                 cmd_len, sizeof(sctx->read_buf));
102                 goto out;
103         }
104
105         data = sctx->read_buf + sizeof(*sctx->cmd_hdr);
106         ret = read_buf(sctx, data, cmd_len);
107         if (ret < 0)
108                 goto out;
109         if (ret) {
110                 ret = -EINVAL;
111                 error("unexpected EOF in stream");
112                 goto out;
113         }
114
115         crc = le32_to_cpu(sctx->cmd_hdr->crc);
116         sctx->cmd_hdr->crc = 0;
117
118         crc2 = crc32c(0, (unsigned char*)sctx->read_buf,
119                         sizeof(*sctx->cmd_hdr) + cmd_len);
120
121         if (crc != crc2) {
122                 ret = -EINVAL;
123                 error("crc32 mismatch in command");
124                 goto out;
125         }
126
127         pos = 0;
128         while (pos < cmd_len) {
129                 struct btrfs_tlv_header *tlv_hdr;
130                 u16 tlv_type;
131                 u16 tlv_len;
132
133                 tlv_hdr = (struct btrfs_tlv_header *)data;
134                 tlv_type = le16_to_cpu(tlv_hdr->tlv_type);
135                 tlv_len = le16_to_cpu(tlv_hdr->tlv_len);
136
137                 if (tlv_type == 0 || tlv_type > BTRFS_SEND_A_MAX
138                     || tlv_len > BTRFS_SEND_BUF_SIZE) {
139                         error("invalid tlv in cmd tlv_type = %hu, tlv_len = %hu",
140                                         tlv_type, tlv_len);
141                         ret = -EINVAL;
142                         goto out;
143                 }
144
145                 sctx->cmd_attrs[tlv_type] = tlv_hdr;
146
147                 data += sizeof(*tlv_hdr) + tlv_len;
148                 pos += sizeof(*tlv_hdr) + tlv_len;
149         }
150
151         sctx->cmd = cmd;
152         ret = 0;
153
154 out:
155         return ret;
156 }
157
158 static int tlv_get(struct btrfs_send_stream *sctx, int attr, void **data, int *len)
159 {
160         int ret;
161         struct btrfs_tlv_header *hdr;
162
163         if (attr <= 0 || attr > BTRFS_SEND_A_MAX) {
164                 error("invalid attribute requested, attr = %d", attr);
165                 ret = -EINVAL;
166                 goto out;
167         }
168
169         hdr = sctx->cmd_attrs[attr];
170         if (!hdr) {
171                 error("attribute %d requested but not present", attr);
172                 ret = -ENOENT;
173                 goto out;
174         }
175
176         *len = le16_to_cpu(hdr->tlv_len);
177         *data = hdr + 1;
178
179         ret = 0;
180
181 out:
182         return ret;
183 }
184
185 #define __TLV_GOTO_FAIL(expr) \
186         if ((ret = expr) < 0) \
187                 goto tlv_get_failed;
188
189 #define __TLV_DO_WHILE_GOTO_FAIL(expr) \
190         do { \
191                 __TLV_GOTO_FAIL(expr) \
192         } while (0)
193
194
195 #define TLV_GET(s, attr, data, len) \
196         __TLV_DO_WHILE_GOTO_FAIL(tlv_get(s, attr, data, len))
197
198 #define TLV_CHECK_LEN(expected, got) \
199         do { \
200                 if (expected != got) { \
201                         error("invalid size for attribute, " \
202                                         "expected = %d, got = %d", \
203                                         (int)expected, (int)got); \
204                         ret = -EINVAL; \
205                         goto tlv_get_failed; \
206                 } \
207         } while (0)
208
209 #define TLV_GET_INT(s, attr, bits, v) \
210         do { \
211                 __le##bits *__tmp; \
212                 int __len; \
213                 TLV_GET(s, attr, (void**)&__tmp, &__len); \
214                 TLV_CHECK_LEN(sizeof(*__tmp), __len); \
215                 *v = get_unaligned_le##bits(__tmp); \
216         } while (0)
217
218 #define TLV_GET_U8(s, attr, v) TLV_GET_INT(s, attr, 8, v)
219 #define TLV_GET_U16(s, attr, v) TLV_GET_INT(s, attr, 16, v)
220 #define TLV_GET_U32(s, attr, v) TLV_GET_INT(s, attr, 32, v)
221 #define TLV_GET_U64(s, attr, v) TLV_GET_INT(s, attr, 64, v)
222
223 static int tlv_get_string(struct btrfs_send_stream *sctx, int attr, char **str)
224 {
225         int ret;
226         void *data;
227         int len = 0;
228
229         TLV_GET(sctx, attr, &data, &len);
230
231         *str = malloc(len + 1);
232         if (!*str)
233                 return -ENOMEM;
234
235         memcpy(*str, data, len);
236         (*str)[len] = 0;
237         ret = 0;
238
239 tlv_get_failed:
240         return ret;
241 }
242 #define TLV_GET_STRING(s, attr, str) \
243         __TLV_DO_WHILE_GOTO_FAIL(tlv_get_string(s, attr, str))
244
245 static int tlv_get_timespec(struct btrfs_send_stream *sctx,
246                             int attr, struct timespec *ts)
247 {
248         int ret;
249         int len;
250         struct btrfs_timespec *bts;
251
252         TLV_GET(sctx, attr, (void**)&bts, &len);
253         TLV_CHECK_LEN(sizeof(*bts), len);
254
255         ts->tv_sec = le64_to_cpu(bts->sec);
256         ts->tv_nsec = le32_to_cpu(bts->nsec);
257         ret = 0;
258
259 tlv_get_failed:
260         return ret;
261 }
262 #define TLV_GET_TIMESPEC(s, attr, ts) \
263         __TLV_DO_WHILE_GOTO_FAIL(tlv_get_timespec(s, attr, ts))
264
265 static int tlv_get_uuid(struct btrfs_send_stream *sctx, int attr, u8 *uuid)
266 {
267         int ret;
268         int len;
269         void *data;
270
271         TLV_GET(sctx, attr, &data, &len);
272         TLV_CHECK_LEN(BTRFS_UUID_SIZE, len);
273         memcpy(uuid, data, BTRFS_UUID_SIZE);
274
275         ret = 0;
276
277 tlv_get_failed:
278         return ret;
279 }
280 #define TLV_GET_UUID(s, attr, uuid) \
281         __TLV_DO_WHILE_GOTO_FAIL(tlv_get_uuid(s, attr, uuid))
282
283 static int read_and_process_cmd(struct btrfs_send_stream *sctx)
284 {
285         int ret;
286         char *path = NULL;
287         char *path_to = NULL;
288         char *clone_path = NULL;
289         char *xattr_name = NULL;
290         void *xattr_data = NULL;
291         void *data = NULL;
292         struct timespec at;
293         struct timespec ct;
294         struct timespec mt;
295         u8 uuid[BTRFS_UUID_SIZE];
296         u8 clone_uuid[BTRFS_UUID_SIZE];
297         u64 tmp;
298         u64 tmp2;
299         u64 ctransid;
300         u64 clone_ctransid;
301         u64 mode;
302         u64 dev;
303         u64 clone_offset;
304         u64 offset;
305         int len;
306         int xattr_len;
307
308         ret = read_cmd(sctx);
309         if (ret)
310                 goto out;
311
312         switch (sctx->cmd) {
313         case BTRFS_SEND_C_SUBVOL:
314                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
315                 TLV_GET_UUID(sctx, BTRFS_SEND_A_UUID, uuid);
316                 TLV_GET_U64(sctx, BTRFS_SEND_A_CTRANSID, &ctransid);
317                 ret = sctx->ops->subvol(path, uuid, ctransid, sctx->user);
318                 break;
319         case BTRFS_SEND_C_SNAPSHOT:
320                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
321                 TLV_GET_UUID(sctx, BTRFS_SEND_A_UUID, uuid);
322                 TLV_GET_U64(sctx, BTRFS_SEND_A_CTRANSID, &ctransid);
323                 TLV_GET_UUID(sctx, BTRFS_SEND_A_CLONE_UUID, clone_uuid);
324                 TLV_GET_U64(sctx, BTRFS_SEND_A_CLONE_CTRANSID, &clone_ctransid);
325                 ret = sctx->ops->snapshot(path, uuid, ctransid, clone_uuid,
326                                 clone_ctransid, sctx->user);
327                 break;
328         case BTRFS_SEND_C_MKFILE:
329                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
330                 ret = sctx->ops->mkfile(path, sctx->user);
331                 break;
332         case BTRFS_SEND_C_MKDIR:
333                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
334                 ret = sctx->ops->mkdir(path, sctx->user);
335                 break;
336         case BTRFS_SEND_C_MKNOD:
337                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
338                 TLV_GET_U64(sctx, BTRFS_SEND_A_MODE, &mode);
339                 TLV_GET_U64(sctx, BTRFS_SEND_A_RDEV, &dev);
340                 ret = sctx->ops->mknod(path, mode, dev, sctx->user);
341                 break;
342         case BTRFS_SEND_C_MKFIFO:
343                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
344                 ret = sctx->ops->mkfifo(path, sctx->user);
345                 break;
346         case BTRFS_SEND_C_MKSOCK:
347                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
348                 ret = sctx->ops->mksock(path, sctx->user);
349                 break;
350         case BTRFS_SEND_C_SYMLINK:
351                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
352                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH_LINK, &path_to);
353                 ret = sctx->ops->symlink(path, path_to, sctx->user);
354                 break;
355         case BTRFS_SEND_C_RENAME:
356                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
357                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH_TO, &path_to);
358                 ret = sctx->ops->rename(path, path_to, sctx->user);
359                 break;
360         case BTRFS_SEND_C_LINK:
361                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
362                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH_LINK, &path_to);
363                 ret = sctx->ops->link(path, path_to, sctx->user);
364                 break;
365         case BTRFS_SEND_C_UNLINK:
366                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
367                 ret = sctx->ops->unlink(path, sctx->user);
368                 break;
369         case BTRFS_SEND_C_RMDIR:
370                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
371                 ret = sctx->ops->rmdir(path, sctx->user);
372                 break;
373         case BTRFS_SEND_C_WRITE:
374                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
375                 TLV_GET_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, &offset);
376                 TLV_GET(sctx, BTRFS_SEND_A_DATA, &data, &len);
377                 ret = sctx->ops->write(path, data, offset, len, sctx->user);
378                 break;
379         case BTRFS_SEND_C_CLONE:
380                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
381                 TLV_GET_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, &offset);
382                 TLV_GET_U64(sctx, BTRFS_SEND_A_CLONE_LEN, &len);
383                 TLV_GET_UUID(sctx, BTRFS_SEND_A_CLONE_UUID, clone_uuid);
384                 TLV_GET_U64(sctx, BTRFS_SEND_A_CLONE_CTRANSID, &clone_ctransid);
385                 TLV_GET_STRING(sctx, BTRFS_SEND_A_CLONE_PATH, &clone_path);
386                 TLV_GET_U64(sctx, BTRFS_SEND_A_CLONE_OFFSET, &clone_offset);
387                 ret = sctx->ops->clone(path, offset, len, clone_uuid,
388                                 clone_ctransid, clone_path, clone_offset,
389                                 sctx->user);
390                 break;
391         case BTRFS_SEND_C_SET_XATTR:
392                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
393                 TLV_GET_STRING(sctx, BTRFS_SEND_A_XATTR_NAME, &xattr_name);
394                 TLV_GET(sctx, BTRFS_SEND_A_XATTR_DATA, &xattr_data, &xattr_len);
395                 ret = sctx->ops->set_xattr(path, xattr_name, xattr_data,
396                                 xattr_len, sctx->user);
397                 break;
398         case BTRFS_SEND_C_REMOVE_XATTR:
399                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
400                 TLV_GET_STRING(sctx, BTRFS_SEND_A_XATTR_NAME, &xattr_name);
401                 ret = sctx->ops->remove_xattr(path, xattr_name, sctx->user);
402                 break;
403         case BTRFS_SEND_C_TRUNCATE:
404                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
405                 TLV_GET_U64(sctx, BTRFS_SEND_A_SIZE, &tmp);
406                 ret = sctx->ops->truncate(path, tmp, sctx->user);
407                 break;
408         case BTRFS_SEND_C_CHMOD:
409                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
410                 TLV_GET_U64(sctx, BTRFS_SEND_A_MODE, &tmp);
411                 ret = sctx->ops->chmod(path, tmp, sctx->user);
412                 break;
413         case BTRFS_SEND_C_CHOWN:
414                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
415                 TLV_GET_U64(sctx, BTRFS_SEND_A_UID, &tmp);
416                 TLV_GET_U64(sctx, BTRFS_SEND_A_GID, &tmp2);
417                 ret = sctx->ops->chown(path, tmp, tmp2, sctx->user);
418                 break;
419         case BTRFS_SEND_C_UTIMES:
420                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
421                 TLV_GET_TIMESPEC(sctx, BTRFS_SEND_A_ATIME, &at);
422                 TLV_GET_TIMESPEC(sctx, BTRFS_SEND_A_MTIME, &mt);
423                 TLV_GET_TIMESPEC(sctx, BTRFS_SEND_A_CTIME, &ct);
424                 ret = sctx->ops->utimes(path, &at, &mt, &ct, sctx->user);
425                 break;
426         case BTRFS_SEND_C_UPDATE_EXTENT:
427                 TLV_GET_STRING(sctx, BTRFS_SEND_A_PATH, &path);
428                 TLV_GET_U64(sctx, BTRFS_SEND_A_FILE_OFFSET, &offset);
429                 TLV_GET_U64(sctx, BTRFS_SEND_A_SIZE, &tmp);
430                 ret = sctx->ops->update_extent(path, offset, tmp, sctx->user);
431                 break;
432         case BTRFS_SEND_C_END:
433                 ret = 1;
434                 break;
435         }
436
437 tlv_get_failed:
438 out:
439         free(path);
440         free(path_to);
441         free(clone_path);
442         free(xattr_name);
443         return ret;
444 }
445
446 /*
447  * If max_errors is 0, then don't stop processing the stream if one of the
448  * callbacks in btrfs_send_ops structure returns an error. If greater than
449  * zero, stop after max_errors errors happened.
450  */
451 int btrfs_read_and_process_send_stream(int fd,
452                                        struct btrfs_send_ops *ops, void *user,
453                                        int honor_end_cmd,
454                                        u64 max_errors)
455 {
456         int ret;
457         struct btrfs_send_stream sctx;
458         struct btrfs_stream_header hdr;
459         u64 errors = 0;
460         int last_err = 0;
461
462         sctx.fd = fd;
463         sctx.ops = ops;
464         sctx.user = user;
465
466         ret = read_buf(&sctx, (char*)&hdr, sizeof(hdr));
467         if (ret < 0)
468                 goto out;
469         if (ret) {
470                 ret = 1;
471                 goto out;
472         }
473
474         if (strcmp(hdr.magic, BTRFS_SEND_STREAM_MAGIC)) {
475                 ret = -EINVAL;
476                 error("unexpected header");
477                 goto out;
478         }
479
480         sctx.version = le32_to_cpu(hdr.version);
481         if (sctx.version > BTRFS_SEND_STREAM_VERSION) {
482                 ret = -EINVAL;
483                 error("stream version %d not supported, please use newer version",
484                                 sctx.version);
485                 goto out;
486         }
487
488         while (1) {
489                 ret = read_and_process_cmd(&sctx);
490                 if (ret < 0) {
491                         last_err = ret;
492                         errors++;
493                         if (max_errors > 0 && errors >= max_errors)
494                                 goto out;
495                 } else if (ret > 0) {
496                         if (!honor_end_cmd)
497                                 ret = 0;
498                         goto out;
499                 }
500         }
501
502 out:
503         if (last_err && !ret)
504                 ret = last_err;
505
506         return ret;
507 }