btrfs-progs: ctree: Add extra level check for read_node_slot()
[platform/upstream/btrfs-progs.git] / send-stream.c
index 450854f..78f2571 100644 (file)
@@ -33,32 +33,54 @@ struct btrfs_send_stream {
        struct btrfs_tlv_header *cmd_attrs[BTRFS_SEND_A_MAX + 1];
        u32 version;
 
+       /*
+        * end of last successful read, equivalent to start of current
+        * malformated part of block
+        */
+       size_t stream_pos;
+
        struct btrfs_send_ops *ops;
        void *user;
 };
 
+/*
+ * Read len bytes to buf.
+ * Return:
+ *   0 - success
+ * < 0 - negative errno in case of error
+ * > 0 - no data read, EOF
+ */
 static int read_buf(struct btrfs_send_stream *sctx, char *buf, size_t len)
 {
        int ret;
        size_t pos = 0;
 
        while (pos < len) {
-               ret = read(sctx->fd, buf + pos, len - pos);
-               if (ret < 0) {
+               ssize_t rbytes;
+
+               rbytes = read(sctx->fd, buf + pos, len - pos);
+               if (rbytes < 0) {
                        ret = -errno;
                        error("read from stream failed: %s",
                                        strerror(-ret));
                        goto out;
                }
-               if (ret == 0) {
+               if (rbytes == 0) {
                        ret = 1;
-                       goto out;
+                       goto out_eof;
                }
-               pos += ret;
+               pos += rbytes;
        }
-
        ret = 0;
 
+out_eof:
+       if (0 < pos && pos < len) {
+               error("short read from stream: expected %zu read %zu", len, pos);
+               ret = -EIO;
+       } else {
+               sctx->stream_pos += pos;
+       }
+
 out:
        return ret;
 }
@@ -66,17 +88,18 @@ out:
 /*
  * Reads a single command from kernel space and decodes the TLV's into
  * sctx->cmd_attrs
+ *
+ * Returns:
+ *   0 - success
+ * < 0 - an error in the command
  */
 static int read_cmd(struct btrfs_send_stream *sctx)
 {
        int ret;
-       int cmd;
-       int cmd_len;
-       int tlv_type;
-       int tlv_len;
+       u16 cmd;
+       u32 cmd_len;
        char *data;
-       int pos;
-       struct btrfs_tlv_header *tlv_hdr;
+       u32 pos;
        u32 crc;
        u32 crc2;
 
@@ -98,7 +121,7 @@ static int read_cmd(struct btrfs_send_stream *sctx)
 
        if (cmd_len + sizeof(*sctx->cmd_hdr) >= sizeof(sctx->read_buf)) {
                ret = -EINVAL;
-               error("command length %d too big for buffer %zu",
+               error("command length %u too big for buffer %zu",
                                cmd_len, sizeof(sctx->read_buf));
                goto out;
        }
@@ -127,13 +150,17 @@ static int read_cmd(struct btrfs_send_stream *sctx)
 
        pos = 0;
        while (pos < cmd_len) {
+               struct btrfs_tlv_header *tlv_hdr;
+               u16 tlv_type;
+               u16 tlv_len;
+
                tlv_hdr = (struct btrfs_tlv_header *)data;
                tlv_type = le16_to_cpu(tlv_hdr->tlv_type);
                tlv_len = le16_to_cpu(tlv_hdr->tlv_len);
 
-               if (tlv_type <= 0 || tlv_type > BTRFS_SEND_A_MAX ||
-                   tlv_len < 0 || tlv_len > BTRFS_SEND_BUF_SIZE) {
-                       error("invalid tlv in cmd tlv_type = %d, tlv_len = %d",
+               if (tlv_type == 0 || tlv_type > BTRFS_SEND_A_MAX
+                   || tlv_len > BTRFS_SEND_BUF_SIZE) {
+                       error("invalid tlv in cmd tlv_type = %hu, tlv_len = %hu",
                                        tlv_type, tlv_len);
                        ret = -EINVAL;
                        goto out;
@@ -155,7 +182,7 @@ out:
 static int tlv_get(struct btrfs_send_stream *sctx, int attr, void **data, int *len)
 {
        int ret;
-       struct btrfs_tlv_header *h;
+       struct btrfs_tlv_header *hdr;
 
        if (attr <= 0 || attr > BTRFS_SEND_A_MAX) {
                error("invalid attribute requested, attr = %d", attr);
@@ -163,15 +190,15 @@ static int tlv_get(struct btrfs_send_stream *sctx, int attr, void **data, int *l
                goto out;
        }
 
-       h = sctx->cmd_attrs[attr];
-       if (!h) {
+       hdr = sctx->cmd_attrs[attr];
+       if (!hdr) {
                error("attribute %d requested but not present", attr);
                ret = -ENOENT;
                goto out;
        }
 
-       *len = le16_to_cpu(h->tlv_len);
-       *data = h + 1;
+       *len = le16_to_cpu(hdr->tlv_len);
+       *data = hdr + 1;
 
        ret = 0;
 
@@ -459,12 +486,13 @@ int btrfs_read_and_process_send_stream(int fd,
        sctx.fd = fd;
        sctx.ops = ops;
        sctx.user = user;
+       sctx.stream_pos = 0;
 
        ret = read_buf(&sctx, (char*)&hdr, sizeof(hdr));
        if (ret < 0)
                goto out;
        if (ret) {
-               ret = 1;
+               ret = -ENODATA;
                goto out;
        }