Esempio n. 1
0
static gboolean
cockpit_ssh_source_dispatch (GSource *source,
                             GSourceFunc callback,
                             gpointer user_data)
{
  CockpitSshSource *cs = (CockpitSshSource *)source;
  CockpitSshTransport *self = cs->transport;
  GIOCondition cond = cs->pfd.revents;
  const gchar *msg;
  gint rc;

  g_return_val_if_fail ((cond & G_IO_NVAL) == 0, FALSE);
  g_assert (self->data != NULL);

  if (self->drain_buffer)
    {
      self->drain_buffer = 0;
      drain_buffer (self);
    }

  if (cond & (G_IO_HUP | G_IO_ERR))
    {
      if (self->sent_close || self->sent_eof)
        {
          self->received_eof = TRUE;
          self->received_close = TRUE;
        }
    }

  /*
   * HACK: Yes this is anohter poll() call. The async support in
   * libssh is quite hacky right now.
   *
   * https://red.libssh.org/issues/155
   */
  rc = ssh_event_dopoll (self->event, 0);
  switch (rc)
    {
    case SSH_OK:
    case SSH_AGAIN:
      break;
    case SSH_ERROR:
      msg = ssh_get_error (self->data->session);

      /*
       * HACK: There doesn't seem to be a way to get at the original socket errno
       * here. So we have to screen scrape.
       *
       * https://red.libssh.org/issues/158
       */
      if (msg && (strstr (msg, "disconnected") ||
                  strstr (msg, "SSH_MSG_DISCONNECT") ||
                  strstr (msg, "Socket error: Success") ||
                  strstr (msg, "Socket error: Connection reset by peer")))
        {
          g_debug ("%s: failed to process channel: %s", self->logname, msg);
          close_immediately (self, "terminated");
        }
      else
        {
          g_message ("%s: failed to process channel: %s", self->logname, msg);
          close_immediately (self, "internal-error");
        }
      return TRUE;
    default:
      g_critical ("%s: ssh_event_dopoll() returned %d", self->logname, rc);
      return FALSE;
    }

  if (cond & G_IO_ERR)
    {
      g_message ("%s: error reading from ssh", self->logname);
      close_immediately (self, "disconnected");
      return TRUE;
    }

  if (self->drain_buffer)
    {
      self->drain_buffer = 0;
      drain_buffer (self);
    }

  if (cond & G_IO_OUT)
    {
      if (!dispatch_queue (self) && self->closing && !self->sent_eof)
        dispatch_eof (self);
      if (self->received_eof && self->sent_eof && !self->sent_close)
        dispatch_close (self);
      if (self->received_close && !self->sent_close)
        dispatch_close (self);
    }

  return TRUE;
}
gboolean
_ostree_static_delta_part_execute (OstreeRepo      *repo,
                                   GVariant        *objects,
                                   GVariant        *part,
                                   gboolean         stats_only,
                                   OstreeDeltaExecuteStats *stats,
                                   GCancellable    *cancellable,
                                   GError         **error)
{
  gboolean ret = FALSE;
  guint8 *checksums_data;
  g_autoptr(GVariant) mode_dict = NULL;
  g_autoptr(GVariant) xattr_dict = NULL;
  g_autoptr(GVariant) payload = NULL;
  g_autoptr(GVariant) ops = NULL;
  StaticDeltaExecutionState statedata = { 0, };
  StaticDeltaExecutionState *state = &statedata;
  guint n_executed = 0;

  static_delta_execution_state_init (&statedata);

  state->repo = repo;
  state->async_error = error;
  state->stats_only = stats_only;

  if (!_ostree_static_delta_parse_checksum_array (objects,
                                                  &checksums_data,
                                                  &state->n_checksums,
                                                  error))
    goto out;

  /* Skip processing for empty delta part */
  if (state->n_checksums == 0)
    {
      ret = TRUE;
      goto out;
    }

  state->checksums = checksums_data;

  g_variant_get (part, "(@a(uuu)@aa(ayay)@ay@ay)",
                 &mode_dict,
                 &xattr_dict,
                 &payload, &ops);

  state->mode_dict = mode_dict;
  state->xattr_dict = xattr_dict;

  state->payload_data = g_variant_get_data (payload);
  state->payload_size = g_variant_get_size (payload);

  state->oplen = g_variant_n_children (ops);
  state->opdata = g_variant_get_data (ops);

  while (state->oplen > 0)
    {
      guint8 opcode;

      opcode = state->opdata[0];
      state->oplen--;
      state->opdata++;

      switch (opcode)
        {
        case OSTREE_STATIC_DELTA_OP_OPEN_SPLICE_AND_CLOSE:
          if (!dispatch_open_splice_and_close (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_OPEN:
          if (!dispatch_open (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_WRITE:
          if (!dispatch_write (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_SET_READ_SOURCE:
          if (!dispatch_set_read_source (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_UNSET_READ_SOURCE:
          if (!dispatch_unset_read_source (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_CLOSE:
          if (!dispatch_close (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_BSPATCH:
          if (!dispatch_bspatch (repo, state, cancellable, error))
            goto out;
          break;
        default:
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
                       "Unknown opcode %u at offset %u", opcode, n_executed);
          goto out;
        }

      n_executed++;
      if (stats)
        stats->n_ops_executed[delta_opcode_index(opcode)]++;
    }

  if (state->caught_error)
    goto out;

  ret = TRUE;
 out:
  g_clear_pointer (&state->content_checksum, g_checksum_free);
  return ret;
}
static gboolean
dispatch_open_splice_and_close (OstreeRepo                 *repo,
                                StaticDeltaExecutionState  *state,
                                GCancellable               *cancellable,  
                                GError                    **error)
{
  gboolean ret = FALSE;

  if (!open_output_target (state, cancellable, error))
    goto out;

  if (OSTREE_OBJECT_TYPE_IS_META (state->output_objtype))
    {
      g_autoptr(GVariant) metadata = NULL;
      guint64 offset;
      guint64 length;

      if (!read_varuint64 (state, &length, error))
        goto out;
      if (!read_varuint64 (state, &offset, error))
        goto out;
      if (!validate_ofs (state, offset, length, error))
        goto out;

      if (state->stats_only)
        {
          ret = TRUE;
          goto out;
        }
      
      metadata = g_variant_new_from_data (ostree_metadata_variant_type (state->output_objtype),
                                          state->payload_data + offset, length, TRUE, NULL, NULL);

      {
        g_autofree guchar *actual_csum = NULL;

        if (!ostree_repo_write_metadata (state->repo, state->output_objtype,
                                         state->checksum,
                                         metadata, &actual_csum,
                                         cancellable,
                                         error))
          goto out;
      }
    }
  else
    {
      guint64 content_offset;
      guint64 objlen;
      g_autoptr(GInputStream) object_input = NULL;
      g_autoptr(GInputStream) memin = NULL;
      
      if (!do_content_open_generic (repo, state, cancellable, error))
        goto out;

      if (!read_varuint64 (state, &state->content_size, error))
        goto out;
      if (!read_varuint64 (state, &content_offset, error))
        goto out;
      if (!validate_ofs (state, content_offset, state->content_size, error))
        goto out;
      
      if (state->stats_only)
        {
          ret = TRUE;
          goto out;
        }

      /* Fast path for regular files to bare repositories */
      if (S_ISREG (state->mode) && 
          (repo->mode == OSTREE_REPO_MODE_BARE ||
           repo->mode == OSTREE_REPO_MODE_BARE_USER))
        {
          if (!_ostree_repo_open_content_bare (repo, state->checksum,
                                               state->content_size,
                                               &state->barecommitstate,
                                               &state->content_out,
                                               &state->have_obj,
                                               cancellable, error))
            goto out;

          if (!state->have_obj)
            {
              if (!handle_untrusted_content_checksum (repo, state, cancellable, error))
                goto out;

              if (!content_out_write (repo, state,
                                      state->payload_data + content_offset,
                                      state->content_size,
                                      cancellable, error))
                goto out;
            }
        }
      else
        {
          /* Slower path, for symlinks and unpacking deltas into archive-z2 */
          g_autoptr(GFileInfo) finfo = NULL;
      
          finfo = _ostree_header_gfile_info_new (state->mode, state->uid, state->gid);

          if (S_ISLNK (state->mode))
            {
              g_autofree char *nulterminated_target =
                g_strndup ((char*)state->payload_data + content_offset, state->content_size);
              g_file_info_set_symlink_target (finfo, nulterminated_target);
            }
          else
            {
              g_assert (S_ISREG (state->mode));
              g_file_info_set_size (finfo, state->content_size);
              memin = g_memory_input_stream_new_from_data (state->payload_data + content_offset, state->content_size, NULL);
            }

          if (!ostree_raw_file_to_content_stream (memin, finfo, state->xattrs,
                                                  &object_input, &objlen,
                                                  cancellable, error))
            goto out;

          {
            g_autofree guchar *actual_csum = NULL;
            if (!ostree_repo_write_content (state->repo,
                                            state->checksum,
                                            object_input,
                                            objlen,
                                            &actual_csum,
                                            cancellable,
                                            error))
              goto out;
          }
        }
    }

  if (!dispatch_close (repo, state, cancellable, error))
    goto out;

  ret = TRUE;
 out:
  if (state->stats_only)
    (void) dispatch_close (repo, state, cancellable, NULL);
  if (!ret)
    g_prefix_error (error, "opcode open-splice-and-close: ");
  return ret;
}
gboolean
_ostree_static_delta_part_execute_raw (OstreeRepo      *repo,
                                       GVariant        *objects,
                                       GVariant        *part,
                                       GCancellable    *cancellable,
                                       GError         **error)
{
  gboolean ret = FALSE;
  guint8 *checksums_data;
  gs_unref_variant GVariant *checksums = NULL;
  gs_unref_variant GVariant *mode_dict = NULL;
  gs_unref_variant GVariant *xattr_dict = NULL;
  gs_unref_variant GVariant *payload = NULL;
  gs_unref_variant GVariant *ops = NULL;
  StaticDeltaExecutionState statedata = { 0, };
  StaticDeltaExecutionState *state = &statedata;
  guint n_executed = 0;

  state->repo = repo;
  state->async_error = error;

  if (!_ostree_static_delta_parse_checksum_array (objects,
                                                  &checksums_data,
                                                  &state->n_checksums,
                                                  error))
    goto out;

  state->checksums = checksums_data;
  g_assert (state->n_checksums > 0);

  g_variant_get (part, "(@a(uuu)@aa(ayay)@ay@ay)",
                 &mode_dict,
                 &xattr_dict,
                 &payload, &ops);

  state->mode_dict = mode_dict;
  state->xattr_dict = xattr_dict;

  state->payload_data = g_variant_get_data (payload);
  state->payload_size = g_variant_get_size (payload);

  state->oplen = g_variant_n_children (ops);
  state->opdata = g_variant_get_data (ops);

  while (state->oplen > 0)
    {
      guint8 opcode;

      opcode = state->opdata[0];
      state->oplen--;
      state->opdata++;

      switch (opcode)
        {
        case OSTREE_STATIC_DELTA_OP_OPEN_SPLICE_AND_CLOSE:
          if (!dispatch_open_splice_and_close (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_OPEN:
          if (!dispatch_open (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_WRITE:
          if (!dispatch_write (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_SET_READ_SOURCE:
          if (!dispatch_set_read_source (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_UNSET_READ_SOURCE:
          if (!dispatch_unset_read_source (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_CLOSE:
          if (!dispatch_close (repo, state, cancellable, error))
            goto out;
          break;
        case OSTREE_STATIC_DELTA_OP_BSPATCH:
          if (!dispatch_bspatch (repo, state, cancellable, error))
            goto out;
          break;
        default:
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
                       "Unknown opcode %u at offset %u", opcode, n_executed);
          goto out;
        }

      n_executed++;
    }

  if (state->caught_error)
    goto out;

  ret = TRUE;
 out:
  return ret;
}