+__attribute__((format (printf, 2, 3)))
+static enum vjson_state vjson_error(char **b, const char *fmt, ...)
+{
+ static char buf[256] = { 0 };
+ const char *pfx = "Firmware image couldn't be validated: ";
+ va_list va;
+ int r;
+
+ r = snprintf(buf, sizeof(buf), "%s", pfx);
+ if (r < 0) {
+ *b = "vjson_error() snprintf failed";
+ return VJSON_ERROR;
+ }
+
+ va_start(va, fmt);
+ r = vsnprintf(buf+r, sizeof(buf)-r, fmt, va);
+ if (r < 0) {
+ *b = "vjson_error() vsnprintf failed";
+ return VJSON_ERROR;
+ }
+ va_end(va);
+
+ *b = buf;
+ return VJSON_ERROR;
+}
+
+static enum vjson_state vjson_parse_token(json_tokener *tok, char *buf, ssize_t len, char **err)
+{
+ json_object *jsobj = NULL;
+
+ jsobj = json_tokener_parse_ex(tok, buf, len);
+ if (json_tokener_get_error(tok) == json_tokener_continue)
+ return VJSON_CONTINUE;
+
+ if (json_tokener_get_error(tok) == json_tokener_success) {
+ if (json_object_get_type(jsobj) != json_type_object) {
+ json_object_put(jsobj);
+ return vjson_error(err, "result is not an JSON object");
+ }
+
+ blobmsg_add_object(&b, jsobj);
+ json_object_put(jsobj);
+ return VJSON_SUCCESS;
+ }
+
+ return vjson_error(err, "failed to parse JSON: %s (%d)",
+ json_tokener_error_desc(json_tokener_get_error(tok)),
+ json_tokener_get_error(tok));
+}
+
+static enum vjson_state vjson_parse(int fd, char **err)
+{
+ enum vjson_state r = VJSON_ERROR;
+ size_t read_count = 0;
+ char buf[64] = { 0 };
+ json_tokener *tok;
+ ssize_t len;
+ int _errno;
+
+ tok = json_tokener_new();
+ if (!tok)
+ return vjson_error(err, "json_tokener_new() failed");
+
+ vjson_error(err, "incomplete JSON input");
+
+ while ((len = read(fd, buf, sizeof(buf)))) {
+ if (len < 0 && errno == EINTR)
+ continue;
+
+ if (len < 0) {
+ _errno = errno;
+ json_tokener_free(tok);
+ return vjson_error(err, "read() failed: %s (%d)",
+ strerror(_errno), _errno);
+ }
+
+ read_count += len;
+ r = vjson_parse_token(tok, buf, len, err);
+ if (r != VJSON_CONTINUE)
+ break;
+
+ memset(buf, 0, sizeof(buf));
+ }
+
+ if (read_count == 0)
+ vjson_error(err, "no JSON input");
+
+ json_tokener_free(tok);
+ return r;
+}
+