/**
 * @file fs_utils.c
 * @brief Multibuild's FS utility functionality implementation
 * @author Iaroslav Makarchuk (i.makarchuk@samsung.com)
 * @date Created Oct 3, 2016
 * @par In Samsung Ukraine R&D Center (SURC) under a contract between
 * @par LLC "Samsung Electronics Ukraine Company" (Kiev, Ukraine) and
 * @par "Samsung Elecrtronics Co", Ltd (Seoul, Republic of Korea)
 * @par Copyright: (c) Samsung Electronics Co, Ltd 2015. All rights reserved.
 *
 * This software is proprietary of Samsung Electronics.
 * No part of this software, either material or conceptual may be copied
 * or distributed, transmitted, transcribed, stored in a retrieval system
 * or translated into any human or computer language in any form by any means,
 * electronic, mechanical, manual or otherwise, or disclosed to third parties
 * without the express written permission of Samsung Electronics.
 *
 * The file was taken from tzsl repo and modified for Multibuild project's
 * needs.
 */

#include <fs_utils.h>

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <pwd.h>

void MultiBuildChown(const char *path, uint32_t path_len,
                     uint32_t uid, uint32_t gid) {
  char tmp_path[MB_FS_MAX_PATH_LEN] = {0};

  if (!path || !path_len || path_len > (sizeof(tmp_path) - 1)) {
    return;
  }

  snprintf(tmp_path, sizeof(tmp_path), "%s", path);

  chown(tmp_path, (uid_t)uid, (gid_t)gid);
}

MultibuildFsResult MultiBuildWriteFileAtomic(const char *path,
                                             size_t path_len,
                                             const void *data,
                                             uint32_t data_len) {
  MultibuildFsResult result = MB_FS_ERROR_BAD_PARAMETERS;
  char tmp_file_path[MB_FS_MAX_PATH_LEN] = {0};
  FILE *tmp_file = NULL;
  size_t written_data_len = 0;

  if (!path || !data || !path_len ||
      !data_len || path_len > (MB_FS_MAX_PATH_LEN - 1)) {
    goto exit;
  }

  snprintf(tmp_file_path, sizeof(tmp_file_path), "%s~", path);

  tmp_file = fopen(tmp_file_path, "wb");

  if (!tmp_file) {
    result = MB_FS_ERROR_GENERIC;
    goto exit;
  }

  written_data_len = fwrite(data, sizeof(unsigned char), data_len, tmp_file);

  fclose(tmp_file);

  if (written_data_len != data_len) {
    unlink(tmp_file_path);
    result = MB_FS_ERROR_GENERIC;
    goto exit;
  }

  if (rename(tmp_file_path, path) < 0) {
    unlink(tmp_file_path);
    result = MB_FS_ERROR_GENERIC;
    goto exit;
  }

  result = MB_FS_OK;

exit:
  return result;
}

MultibuildFsResult MultiBuildReadFile(const char *path,
                                      size_t path_len,
                                      void *data,
                                      uint32_t *data_len) {
  MultibuildFsResult result = MB_FS_ERROR_BAD_PARAMETERS;
  char tmp_file_path[MB_FS_MAX_PATH_LEN] = {0};
  FILE *tmp_file = NULL;
  size_t read_data_len = 0;
  size_t read_res = 0;
  struct stat tmp_file_stat;

  if (!path || !data || !path_len ||
      !data_len || !*data_len || path_len > (MB_FS_MAX_PATH_LEN - 1)) {
    goto exit;
  }

  snprintf(tmp_file_path, sizeof(tmp_file_path), "%s", path);

  tmp_file = fopen(tmp_file_path, "rb");

  if (!tmp_file) {
    result = MB_FS_ERROR_NOT_FOUND;
    goto exit;
  }

  if ((stat(tmp_file_path, &tmp_file_stat)) || (!S_ISREG(tmp_file_stat.st_mode))) {
    result = MB_FS_ERROR_GENERIC;
    goto exit;
  }

  if (*data_len < tmp_file_stat.st_size) {
    result = MB_FS_ERROR_SHORT_BUFFER;
    goto exit;
  }

  do {
    read_res = fread((char *)data + read_data_len, sizeof(unsigned char),
                     *data_len - read_data_len, tmp_file);
    read_data_len += read_res;
  }  while (read_res > 0);

  if (read_data_len != (size_t)tmp_file_stat.st_size) {
    result = MB_FS_ERROR_GENERIC;
    goto exit;
  }

  *data_len = read_data_len;
  result = MB_FS_OK;

exit:
  if (tmp_file) {
    fclose(tmp_file);
  }

  return result;
}
