#include "xattr.h"
#include "nwd_log.h"

#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/xattr.h>


struct LV {
  uint16_t length;
  uint8_t data[];
};

#ifndef F_LINUX_SPECIFIC_BASE
#define F_LINUX_SPECIFIC_BASE   (1024)
#endif
#define F_FIVE_PA_SETXATTR      (F_LINUX_SPECIFIC_BASE + 103)


PaResult XattrHasAttr(const char *filename, const char *attribute,
                      size_t *value_size) {
  if (!filename || !attribute) {
    LOG_E("Invalid arguments.\n");
    return PA_INVALID_ARGUMENTS;
  }

  ssize_t size = getxattr(filename, attribute, NULL, 0);

  if (size < 0) {
    LOG_V("File %s has not %s attribute.\n", filename, attribute);
    return PA_GENERAL_ERROR;
  }

  if (value_size) {
    *value_size = size;
  }

  return PA_SUCCESS;
}

PaResult XattrRead(const char *filename, const char *attribute, void **value,
                   size_t *value_size) {
  if (!filename || !attribute || !value || !value_size) {
    LOG_E("Invalid arguments.\n");
    return PA_INVALID_ARGUMENTS;
  }

  ssize_t size = getxattr(filename, attribute, NULL, 0);

  if (size < 0) {
    LOG_V("File %s has not %s attribute.\n", filename, attribute);
    return PA_GENERAL_ERROR;
  }

  void *data = malloc(size);
  if (!data) {
    LOG_E("Unable allocate the memory\n");
    return PA_MALLOC_ERROR;
  }

  size = getxattr(filename, attribute, data, size);
  if (size < 0) {
    LOG_E("Can not get extended-attribute\n");
    free(data);
    return PA_GENERAL_ERROR;
  }

  *value = data;
  *value_size = size;

  LOG_V("file: %s, attr: %s, size: %u is read.\n", filename, attribute, size);

  LOG_V("xattr value is:\n");
  LOGM_V(data, size);

  return PA_SUCCESS;
}

PaResult XattrWrite(const char *filename, const char *attribute,
                    const void *value, size_t value_size) {
  if (!filename || !attribute || !value) {
    LOG_E("Invalid arguments.\n");
    return PA_INVALID_ARGUMENTS;
  }

  int res = setxattr(filename, attribute, value, value_size, 0);
  if (res) {
    LOG_E("Can not set extended-attribute.\n");
    LOG_D("Received error: %d\n", res);
    return PA_GENERAL_ERROR;
  }

  return PA_SUCCESS;
}

PaResult XattrFdHasAttr(int fd, const char *attribute,
                        size_t *value_size) {
  if (!attribute) {
    LOG_E("Invalid arguments.\n");
    return PA_INVALID_ARGUMENTS;
  }

  ssize_t size = fgetxattr(fd, attribute, NULL, 0);

  if (size < 0) {
    LOG_V("Fd %d has not %s attribute.\n", fd, attribute);
    return PA_GENERAL_ERROR;
  }

  if (value_size) {
    *value_size = size;
  }

  return PA_SUCCESS;
}

PaResult XattrFdWrite(int fd, const char *attribute,
                      const void *value, size_t value_size) {
  if (!attribute || !value) {
    LOG_E("Invalid arguments.\n");
    return PA_INVALID_ARGUMENTS;
  }

  int res = fsetxattr(fd, attribute, value, value_size, 0);
  if (res) {
    LOG_E("Can not set extended-attribute.\n");
    LOG_D("Received error: %d\n", res);
    return PA_GENERAL_ERROR;
  }

  return PA_SUCCESS;
}

PaResult XattrUserPaFcntl(int fd, const void *value, size_t value_size) {
  if (!value) {
    LOG_E("Invalid arguments.\n");
    return PA_INVALID_ARGUMENTS;
  }

  struct LV *lv = malloc(value_size + sizeof(struct LV));
  if (!lv) {
    return PA_MALLOC_ERROR;
  }

  lv->length = value_size;
  memcpy(lv->data, value, value_size);

  int res = fcntl(fd, F_FIVE_PA_SETXATTR, lv);
  free(lv);
  if (res) {
    LOG_E("Can not set extended-attribute via F_FIVE_PA_SETXATTR.\n");
    LOG_D("Received error: %d\n", res);
    return PA_GENERAL_ERROR;
  }

  return PA_SUCCESS;
}
