Exemplo n.º 1
0
int
ipmi_sdr_cache_open (ipmi_sdr_ctx_t ctx,
                     ipmi_ctx_t ipmi_ctx,
                     const char *filename)
{
  uint8_t sdr_version;
  uint16_t record_count;
  uint32_t most_recent_addition_timestamp, most_recent_erase_timestamp;
  char sdr_version_buf;
  char sdr_cache_magic_buf[4];
  char sdr_cache_version_buf[4];
  char record_count_buf[2];
  char most_recent_addition_timestamp_buf[4];
  char most_recent_erase_timestamp_buf[4];
  struct stat stat_buf;

  if (!ctx || ctx->magic != IPMI_SDR_CTX_MAGIC)
    {
      ERR_TRACE (ipmi_sdr_ctx_errormsg (ctx), ipmi_sdr_ctx_errnum (ctx));
      return (-1);
    }

  if (!filename)
    {
      SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_PARAMETERS);
      return (-1);
    }

  if (ctx->operation != IPMI_SDR_OPERATION_UNINITIALIZED)
    {
      if (ctx->operation == IPMI_SDR_OPERATION_READ_CACHE)
        SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_READ_ALREADY_INITIALIZED);
      else
        SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_INTERNAL_ERROR);
      return (-1);
    }

  if (stat (filename, &stat_buf) < 0)
    {
      SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
      goto cleanup;
    }

  /* File Size must be atleast magic_buf + file_version_buf +
   * sdr_version_buf + record_count_buf +
   * most_recent_addition_timestamp_buf +
   * most_recent_erase_timestamp-buf in size.
   */

  ctx->file_size = stat_buf.st_size;
  if (ctx->file_size < (4 + 4 + 1 + 2 + 4 + 4))
    {
      SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_INVALID);
      goto cleanup;
    }

  if ((ctx->fd = open (filename, O_RDONLY)) < 0)
    {
      SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
      goto cleanup;
    }

  ctx->sdr_cache = (uint8_t *)mmap (NULL,
                                    ctx->file_size,
                                    PROT_READ,
                                    MAP_PRIVATE,
                                    ctx->fd,
                                    0);
  if (!ctx->sdr_cache || ctx->sdr_cache == ((void *) -1))
    {
      ERRNO_TRACE (errno);
      SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
      goto cleanup;
    }

  memcpy (sdr_cache_magic_buf, ctx->sdr_cache + ctx->records_start_offset, 4);
  ctx->records_start_offset += 4;
  memcpy (sdr_cache_version_buf, ctx->sdr_cache + ctx->records_start_offset, 4);
  ctx->records_start_offset += 4;
  memcpy (&sdr_version_buf, ctx->sdr_cache + ctx->records_start_offset, 1);
  ctx->records_start_offset += 1;
  memcpy (record_count_buf, ctx->sdr_cache + ctx->records_start_offset, 2);
  ctx->records_start_offset += 2;
  memcpy (most_recent_addition_timestamp_buf, ctx->sdr_cache + ctx->records_start_offset, 4);
  ctx->records_start_offset += 4;
  memcpy (most_recent_erase_timestamp_buf, ctx->sdr_cache + ctx->records_start_offset, 4);
  ctx->records_start_offset += 4;

  if ((uint8_t)sdr_cache_magic_buf[0] != IPMI_SDR_CACHE_FILE_MAGIC_0
      || (uint8_t)sdr_cache_magic_buf[1] != IPMI_SDR_CACHE_FILE_MAGIC_1
      || (uint8_t)sdr_cache_magic_buf[2] != IPMI_SDR_CACHE_FILE_MAGIC_2
      || (uint8_t)sdr_cache_magic_buf[3] != IPMI_SDR_CACHE_FILE_MAGIC_3)
    {
      SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_INVALID);
      goto cleanup;
    }

  if (((uint8_t)sdr_cache_version_buf[0] != IPMI_SDR_CACHE_FILE_VERSION_1_0
       || (uint8_t)sdr_cache_version_buf[1] != IPMI_SDR_CACHE_FILE_VERSION_1_1
       || (uint8_t)sdr_cache_version_buf[2] != IPMI_SDR_CACHE_FILE_VERSION_1_2
       || (uint8_t)sdr_cache_version_buf[3] != IPMI_SDR_CACHE_FILE_VERSION_1_3)
      && ((uint8_t)sdr_cache_version_buf[0] != IPMI_SDR_CACHE_FILE_VERSION_1_2_0
	  || (uint8_t)sdr_cache_version_buf[1] != IPMI_SDR_CACHE_FILE_VERSION_1_2_1
	  || (uint8_t)sdr_cache_version_buf[2] != IPMI_SDR_CACHE_FILE_VERSION_1_2_2
	  || (uint8_t)sdr_cache_version_buf[3] != IPMI_SDR_CACHE_FILE_VERSION_1_2_3))
    {
      SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_INVALID);
      goto cleanup;
    }

  ctx->sdr_version = (uint8_t)sdr_version_buf;
  ctx->record_count = ((uint16_t)record_count_buf[0] & 0xFF);
  ctx->record_count |= ((uint16_t)record_count_buf[1] & 0xFF) << 8;
  ctx->most_recent_addition_timestamp = ((uint32_t)most_recent_addition_timestamp_buf[0] & 0xFF);
  ctx->most_recent_addition_timestamp |= ((uint32_t)most_recent_addition_timestamp_buf[1] & 0xFF) << 8;
  ctx->most_recent_addition_timestamp |= ((uint32_t)most_recent_addition_timestamp_buf[2] & 0xFF) << 16;
  ctx->most_recent_addition_timestamp |= ((uint32_t)most_recent_addition_timestamp_buf[3] & 0xFF) << 24;
  ctx->most_recent_erase_timestamp = ((uint32_t)most_recent_erase_timestamp_buf[0] & 0xFF);
  ctx->most_recent_erase_timestamp |= ((uint32_t)most_recent_erase_timestamp_buf[1] & 0xFF) << 8;
  ctx->most_recent_erase_timestamp |= ((uint32_t)most_recent_erase_timestamp_buf[2] & 0xFF) << 16;
  ctx->most_recent_erase_timestamp |= ((uint32_t)most_recent_erase_timestamp_buf[3] & 0xFF) << 24;

  if (ipmi_ctx)
    {
      if (sdr_info (ctx,
		    ipmi_ctx,
		    &sdr_version,
		    &record_count,
		    &most_recent_addition_timestamp,
		    &most_recent_erase_timestamp) < 0)
	goto cleanup;

      if (ctx->sdr_version != sdr_version
	  || ctx->most_recent_addition_timestamp != most_recent_addition_timestamp
	  || ctx->most_recent_erase_timestamp != most_recent_erase_timestamp)
	{
	  SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_OUT_OF_DATE);
	  goto cleanup;
	}
    }

  if ((uint8_t)sdr_cache_version_buf[0] == IPMI_SDR_CACHE_FILE_VERSION_1_2_0
      && (uint8_t)sdr_cache_version_buf[1] == IPMI_SDR_CACHE_FILE_VERSION_1_2_1
      && (uint8_t)sdr_cache_version_buf[2] == IPMI_SDR_CACHE_FILE_VERSION_1_2_2
      && (uint8_t)sdr_cache_version_buf[3] == IPMI_SDR_CACHE_FILE_VERSION_1_2_3)
    {
      uint8_t header_checksum_buf[512];
      unsigned int header_checksum_buf_len = 0;
      uint8_t header_checksum, header_checksum_cache;
      uint8_t trailer_checksum, trailer_checksum_cache;
      char total_bytes_written_buf[4];
      unsigned int total_bytes_written;
      unsigned int header_bytes_len;
      unsigned int trailer_bytes_len;

      /* File Size must be atleast magic_buf + file_version_buf +
       * sdr_version_buf + record_count_buf +
       * most_recent_addition_timestamp_buf +
       * most_recent_erase_timestamp-buf + header_checksum + trailer
       * bytes written + trailer records checksum.
       */
      
      header_bytes_len = 4 + 4 + 1 + 2 + 4 + 4 + 1;
      trailer_bytes_len = 4 + 1;

      if (ctx->file_size < (header_bytes_len + trailer_bytes_len))
	{
	  SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_INVALID);
	  goto cleanup;
	}

      memcpy (&header_checksum_cache, ctx->sdr_cache + ctx->records_start_offset, 1);
      ctx->records_start_offset += 1;

      memcpy(&header_checksum_buf[header_checksum_buf_len], sdr_cache_magic_buf, 4);
      header_checksum_buf_len += 4;
      memcpy(&header_checksum_buf[header_checksum_buf_len], sdr_cache_version_buf, 4);
      header_checksum_buf_len += 4;
      memcpy(&header_checksum_buf[header_checksum_buf_len], &sdr_version_buf, 1);
      header_checksum_buf_len += 1;
      memcpy(&header_checksum_buf[header_checksum_buf_len], record_count_buf, 2);
      header_checksum_buf_len += 2;
      memcpy(&header_checksum_buf[header_checksum_buf_len], most_recent_addition_timestamp_buf, 4);
      header_checksum_buf_len += 4;
      memcpy(&header_checksum_buf[header_checksum_buf_len], most_recent_erase_timestamp_buf, 4);
      header_checksum_buf_len += 4;
      
      header_checksum = ipmi_checksum (header_checksum_buf, header_checksum_buf_len);
      if (header_checksum != header_checksum_cache)
	{
	  SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_INVALID);
	  goto cleanup;
	}

      /* total_bytes_written is written before checksum */
      /* checksum of records is last byte written */
      memcpy (total_bytes_written_buf, ctx->sdr_cache + ctx->file_size - 5, 4);
      memcpy (&trailer_checksum_cache, ctx->sdr_cache + ctx->file_size - 1, 1);

      total_bytes_written = ((uint32_t)total_bytes_written_buf[0] & 0xFF);
      total_bytes_written |= ((uint32_t)total_bytes_written_buf[1] & 0xFF) << 8;
      total_bytes_written |= ((uint32_t)total_bytes_written_buf[2] & 0xFF) << 16;
      total_bytes_written |= ((uint32_t)total_bytes_written_buf[3] & 0xFF) << 24;

      if (total_bytes_written != ctx->file_size)
	{
	  SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_INVALID);
	  goto cleanup;
	}

      /* -1 for checksum */
      trailer_checksum = ipmi_checksum (ctx->sdr_cache + ctx->records_start_offset,
					total_bytes_written - ctx->records_start_offset - 1);

      if (trailer_checksum != trailer_checksum_cache)
	{
	  SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_INVALID);
	  goto cleanup;
	}

      ctx->records_end_offset = ctx->file_size - trailer_bytes_len;
    }
  else /* (uint8_t)sdr_cache_version_buf[0] == IPMI_SDR_CACHE_FILE_VERSION_1_0
	  && (uint8_t)sdr_cache_version_buf[1] == IPMI_SDR_CACHE_FILE_VERSION_1_1
	  && (uint8_t)sdr_cache_version_buf[2] == IPMI_SDR_CACHE_FILE_VERSION_1_2
	  && (uint8_t)sdr_cache_version_buf[3] == IPMI_SDR_CACHE_FILE_VERSION_1_3 */
    ctx->records_end_offset = ctx->file_size;

  _sdr_set_current_offset (ctx, ctx->records_start_offset);
  ctx->operation = IPMI_SDR_OPERATION_READ_CACHE;
  ctx->errnum = IPMI_SDR_ERR_SUCCESS;
  return (0);

 cleanup:
  /* ignore potential error, cleanup path */
  if (ctx->fd >= 0)
    close (ctx->fd);
  /* ignore potential error, cleanup path */
  if (ctx->sdr_cache)
    munmap ((void *)ctx->sdr_cache, ctx->file_size);
  sdr_init_ctx (ctx);
  return (-1);
}
int
ipmi_sdr_cache_create (ipmi_sdr_ctx_t ctx,
                       ipmi_ctx_t ipmi_ctx,
                       const char *filename,
                       int cache_create_flags,
                       Ipmi_Sdr_Cache_Create_Callback create_callback,
                       void *create_callback_data)
{
    int open_flags;
    uint8_t sdr_version;
    uint16_t record_count, reservation_id, record_id, next_record_id;
    uint32_t most_recent_addition_timestamp, most_recent_erase_timestamp;
    unsigned int record_count_written = 0;
    unsigned int total_bytes_written = 0;
    uint16_t *record_ids = NULL;
    unsigned int record_ids_count = 0;
    unsigned int cache_create_flags_mask = (IPMI_SDR_CACHE_CREATE_FLAGS_OVERWRITE
                                            | IPMI_SDR_CACHE_CREATE_FLAGS_DUPLICATE_RECORD_ID
                                            | IPMI_SDR_CACHE_CREATE_FLAGS_ASSUME_MAX_SDR_RECORD_COUNT);
    uint8_t trailer_checksum = 0;
    int fd = -1;
    int rv = -1;

    if (!ctx || ctx->magic != IPMI_SDR_CTX_MAGIC)
    {
        ERR_TRACE (ipmi_sdr_ctx_errormsg (ctx), ipmi_sdr_ctx_errnum (ctx));
        return (-1);
    }

    /* Version cannot be 0h according to the IPMI spec */
    if (!ipmi_ctx
            || !filename
            || (strlen (filename) > MAXPATHLEN)
            || (cache_create_flags & ~cache_create_flags_mask))
    {
        SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_PARAMETERS);
        return (-1);
    }

    if (ctx->operation != IPMI_SDR_OPERATION_UNINITIALIZED)
    {
        if (ctx->operation == IPMI_SDR_OPERATION_READ_CACHE)
            SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CONTEXT_PERFORMING_OTHER_OPERATION);
        else
            SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_INTERNAL_ERROR);
        return (-1);
    }

    ctx->operation = IPMI_SDR_OPERATION_CREATE_CACHE;

    if (cache_create_flags & IPMI_SDR_CACHE_CREATE_FLAGS_OVERWRITE)
        open_flags = O_CREAT | O_TRUNC | O_WRONLY;
    else
        open_flags = O_CREAT | O_EXCL | O_WRONLY;

    if ((fd = open (filename, open_flags, 0644)) < 0)
    {
        if (!(cache_create_flags & IPMI_SDR_CACHE_CREATE_FLAGS_OVERWRITE)
                && errno == EEXIST)
            SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_CREATE_CACHE_EXISTS);
        else if (errno == EPERM
                 || errno == EACCES
                 || errno == EISDIR
                 || errno == EROFS)
            SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_PERMISSION);
        else if (errno == ENAMETOOLONG
                 || errno == ENOENT
                 || errno == ELOOP)
            SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_FILENAME_INVALID);
        else if (errno == ENOSPC
                 || errno == EMFILE
                 || errno == ENFILE)
            SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_FILESYSTEM);
        else
            SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
        goto cleanup;
    }

    if (sdr_info (ctx,
                  ipmi_ctx,
                  &sdr_version,
                  &record_count,
                  &most_recent_addition_timestamp,
                  &most_recent_erase_timestamp) < 0)
        goto cleanup;

    if (!record_count)
    {
        SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_CREATE_INVALID_RECORD_COUNT);
        goto cleanup;
    }

    if (_sdr_cache_header_write (ctx,
                                 ipmi_ctx,
                                 fd,
                                 &total_bytes_written,
                                 sdr_version,
                                 record_count,
                                 most_recent_addition_timestamp,
                                 most_recent_erase_timestamp) < 0)
        goto cleanup;

    /* Version cannot be 0h according to the IPMI spec, but we accept it regardless */
    ctx->sdr_version = sdr_version;
    ctx->record_count = record_count;
    ctx->most_recent_addition_timestamp = most_recent_addition_timestamp;
    ctx->most_recent_erase_timestamp = most_recent_erase_timestamp;

    if (cache_create_flags & IPMI_SDR_CACHE_CREATE_FLAGS_DUPLICATE_RECORD_ID)
    {
        if (!(record_ids = (uint16_t *)malloc (ctx->record_count * sizeof (uint16_t))))
        {
            SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_OUT_OF_MEMORY);
            goto cleanup;
        }
        record_ids_count = 0;
    }

    if (_sdr_cache_reservation_id (ctx,
                                   ipmi_ctx,
                                   &reservation_id) < 0)
        goto cleanup;

    next_record_id = IPMI_SDR_RECORD_ID_FIRST;
    while (next_record_id != IPMI_SDR_RECORD_ID_LAST)
    {
        uint8_t record_buf[IPMI_SDR_MAX_RECORD_LENGTH];
        int record_len;

        if (record_count_written >= ctx->record_count)
        {
            /* IPMI Workaround
             *
             * Discovered on unspecified Inspur motherboard
             *
             * SDR record reading is broken, the IPMI_SDR_RECORD_ID_LAST
             * record id never occurs.  So this workaround allows the
             * user to not error out and avoids this loop from looping
             * infinitely.
             *
             */

            if (cache_create_flags & IPMI_SDR_CACHE_CREATE_FLAGS_ASSUME_MAX_SDR_RECORD_COUNT)
                break;

            SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_CREATE_INVALID_RECORD_COUNT);
            goto cleanup;
        }

        record_id = next_record_id;
        if ((record_len = _sdr_cache_get_record (ctx,
                          ipmi_ctx,
                          record_id,
                          record_buf,
                          IPMI_SDR_MAX_RECORD_LENGTH,
                          &reservation_id,
                          &next_record_id)) < 0)
            goto cleanup;

        if (record_len)
        {
            if (ctx->flags & IPMI_SDR_FLAGS_DEBUG_DUMP)
            {
                const char *record_str;

                if ((record_str = sdr_record_type_str (ctx,
                                                       record_buf,
                                                       record_len)))
                {
                    char hdrbuf[IPMI_SDR_CACHE_DEBUG_BUFLEN];

                    debug_hdr_str (DEBUG_UTIL_TYPE_NONE,
                                   DEBUG_UTIL_DIRECTION_NONE,
                                   DEBUG_UTIL_FLAGS_DEFAULT,
                                   record_str,
                                   hdrbuf,
                                   IPMI_SDR_CACHE_DEBUG_BUFLEN);

                    ipmi_dump_sdr_record (STDERR_FILENO,
                                          ctx->debug_prefix,
                                          hdrbuf,
                                          NULL,
                                          record_buf,
                                          record_len);
                }
            }

            if (_sdr_cache_record_write (ctx,
                                         fd,
                                         &total_bytes_written,
                                         record_ids,
                                         &record_ids_count,
                                         record_buf,
                                         record_len,
                                         &trailer_checksum) < 0)
                goto cleanup;

            record_count_written++;

            if (create_callback)
                (*create_callback)(ctx->sdr_version,
                                   ctx->record_count,
                                   ctx->most_recent_addition_timestamp,
                                   ctx->most_recent_erase_timestamp,
                                   record_id,
                                   create_callback_data);
        }
    }

    if (record_count_written != ctx->record_count)
    {
        /*
         * IPMI Workaround (achu)
         *
         * Discovered on Fujitsu RX 100
         * Discovered on Fujitsu RX300/200-S8
         *
         * The record_count listed from the Get SDR Repository Info command
         * is not consistent with the length of SDR records stored.
         *
         * We will assume that if we reached the end of the SDR record
         * list (i.e. next_record_id == 0xFFFF), a non-zero number of
         * records were written, it's ok and we can continue on.
         */
        if (next_record_id == IPMI_SDR_RECORD_ID_LAST
                && record_count_written)
        {
            unsigned int total_bytes_written_temp = 0;

            ctx->record_count = record_count_written;

            /* need to seek back to the beginning of the file and
             * re-write the header info with the correct number of
             * records
             */

            if (lseek (fd, 0, SEEK_SET) < 0)
            {
                SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
                goto cleanup;
            }

            if (_sdr_cache_header_write (ctx,
                                         ipmi_ctx,
                                         fd,
                                         &total_bytes_written_temp,
                                         ctx->sdr_version,
                                         ctx->record_count,
                                         ctx->most_recent_addition_timestamp,
                                         ctx->most_recent_erase_timestamp) < 0)
                goto cleanup;

            /* need to seek back to the end of the file to write the
             * trailer below
             */
            if (lseek (fd, 0, SEEK_END) < 0)
            {
                SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_SYSTEM_ERROR);
                goto cleanup;
            }
        }
        else
        {
            SDR_SET_ERRNUM (ctx, IPMI_SDR_ERR_CACHE_CREATE_INVALID_RECORD_COUNT);
            goto cleanup;
        }
    }

    if (_sdr_cache_trailer_write (ctx,
                                  ipmi_ctx,
                                  fd,
                                  total_bytes_written,
                                  trailer_checksum) < 0)
        goto cleanup;

    if (fsync (fd) < 0)
    {
        SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
        goto cleanup;
    }

    if (close (fd) < 0)
    {
        SDR_ERRNO_TO_SDR_ERRNUM (ctx, errno);
        goto cleanup;
    }
    fd = -1;

    rv = 0;
    ctx->errnum = IPMI_SDR_ERR_SUCCESS;
cleanup:
    ctx->operation = IPMI_SDR_OPERATION_UNINITIALIZED;
    if (fd >= 0)
    {
        /* If the cache create never completed, try to remove the file */
        /* ignore potential error, cleanup path */
        unlink (filename);
        /* ignore potential error, cleanup path */
        close (fd);
    }
    free (record_ids);
    sdr_init_ctx (ctx);
    return (rv);
}