/* nwarchive.c - mars_nwe private archive metadata helpers */
#include "net.h"
#include "nwarchive.h"

#if XATTR_SUPPORT
#include <errno.h>
#include <sys/types.h>
#include <sys/xattr.h>
#endif

#define MARS_NWE_ARCHIVE_XATTR "user.mars_nwe.archive"
#define MARS_NWE_ARCHIVE_VERSION 1

typedef struct {
  uint8 version;
  uint8 flags;
  uint8 archive_date[2];
  uint8 archive_time[2];
  uint8 archiver_id[4];
} MARS_NWE_ARCHIVE_XATTR_DATA;

static void init_archive_data(MARS_NWE_ARCHIVE_XATTR_DATA *d)
{
  memset(d, 0, sizeof(*d));
  d->version = MARS_NWE_ARCHIVE_VERSION;
}

static void read_archive_data(MARS_NWE_ARCHIVE_XATTR_DATA *d,
                              uint16 *archive_date,
                              uint16 *archive_time,
                              uint32 *archiver_id,
                              uint8 *flags)
{
  if (flags)
    *flags = d->flags;
  if (archive_date)
    *archive_date = (d->flags & MARS_NWE_ARCHIVE_HAS_DATE)
                  ? (uint16)GET_16(d->archive_date) : 0;
  if (archive_time)
    *archive_time = (d->flags & MARS_NWE_ARCHIVE_HAS_TIME)
                  ? (uint16)GET_16(d->archive_time) : 0;
  if (archiver_id)
    *archiver_id = (d->flags & MARS_NWE_ARCHIVE_HAS_ARCHIVER)
                 ? GET_BE32(d->archiver_id) : 0;
}

#if XATTR_SUPPORT
static int get_archive_data(char *unixname, MARS_NWE_ARCHIVE_XATTR_DATA *d)
{
  ssize_t len;

  init_archive_data(d);
  if (!unixname || !*unixname)
    return(-1);

  len = getxattr(unixname, MARS_NWE_ARCHIVE_XATTR, d, sizeof(*d));
  if (len != sizeof(*d) || d->version != MARS_NWE_ARCHIVE_VERSION) {
    init_archive_data(d);
    return(-1);
  }
  return(0);
}
#endif

void mars_nwe_get_archive_info(char *unixname,
                               uint16 *archive_date,
                               uint16 *archive_time,
                               uint32 *archiver_id,
                               uint8 *flags)
{
  MARS_NWE_ARCHIVE_XATTR_DATA d;

#if XATTR_SUPPORT
  (void)get_archive_data(unixname, &d);
#else
  (void)unixname;
  init_archive_data(&d);
#endif
  read_archive_data(&d, archive_date, archive_time, archiver_id, flags);
}

int mars_nwe_set_archive_info(char *unixname,
                              int set_date, uint16 archive_date,
                              int set_time, uint16 archive_time,
                              int set_archiver, uint32 archiver_id)
{
#if XATTR_SUPPORT
  MARS_NWE_ARCHIVE_XATTR_DATA d;

  (void)get_archive_data(unixname, &d);

  if (set_date) {
    U16_TO_16(archive_date, d.archive_date);
    if (archive_date)
      d.flags |= MARS_NWE_ARCHIVE_HAS_DATE;
    else
      d.flags &= (uint8)~MARS_NWE_ARCHIVE_HAS_DATE;
  }
  if (set_time) {
    U16_TO_16(archive_time, d.archive_time);
    if (archive_time)
      d.flags |= MARS_NWE_ARCHIVE_HAS_TIME;
    else
      d.flags &= (uint8)~MARS_NWE_ARCHIVE_HAS_TIME;
  }
  if (set_archiver) {
    U32_TO_BE32(archiver_id, d.archiver_id);
    if (archiver_id)
      d.flags |= MARS_NWE_ARCHIVE_HAS_ARCHIVER;
    else
      d.flags &= (uint8)~MARS_NWE_ARCHIVE_HAS_ARCHIVER;
  }

  if (!unixname || !*unixname)
    return(0);

  if (!d.flags) {
    if (removexattr(unixname, MARS_NWE_ARCHIVE_XATTR)) {
      int err = errno;
      if (err != ENODATA
#ifdef ENOATTR
          && err != ENOATTR
#endif
         )
        XDPRINTF((5,0,"mars_nwe archive xattr remove ignored for %s errno=%d", unixname, err));
    }
    return(0);
  }

  if (setxattr(unixname, MARS_NWE_ARCHIVE_XATTR, &d, sizeof(d), 0)) {
    int err = errno;
    /* Compatibility rule: NetWare archive metadata has no POSIX field.
     * If Linux xattrs are unavailable or rejected, keep the NCP call
     * successful and only log that the extra metadata was not stored.
     */
    XDPRINTF((5,0,"mars_nwe archive xattr ignored for %s errno=%d", unixname, err));
  }
#else
  (void)unixname;
  (void)set_date;
  (void)archive_date;
  (void)set_time;
  (void)archive_time;
  (void)set_archiver;
  (void)archiver_id;
#endif
  return(0);
}


#define MARS_NWE_FILEINFO_XATTR "user.mars_nwe.fileinfo"
#define MARS_NWE_FILEINFO_VERSION 1

typedef struct {
  uint8 version;
  uint8 flags;
  uint8 create_date[2];
  uint8 create_time[2];
  uint8 creator_id[4];
  uint8 modifier_id[4];
} MARS_NWE_FILEINFO_XATTR_DATA;

#define MARS_NWE_FILEINFO_XATTR_V1_SIZE 10

static void init_file_info_data(MARS_NWE_FILEINFO_XATTR_DATA *d)
{
  memset(d, 0, sizeof(*d));
  d->version = MARS_NWE_FILEINFO_VERSION;
}

static void read_file_info_data(MARS_NWE_FILEINFO_XATTR_DATA *d,
                                uint16 *create_date,
                                uint16 *create_time,
                                uint32 *creator_id,
                                uint32 *modifier_id,
                                uint8 *flags)
{
  if (flags)
    *flags = d->flags;
  if (create_date)
    *create_date = (d->flags & MARS_NWE_FILEINFO_HAS_CREATE_DATE)
                 ? (uint16)GET_16(d->create_date) : 0;
  if (create_time)
    *create_time = (d->flags & MARS_NWE_FILEINFO_HAS_CREATE_TIME)
                 ? (uint16)GET_16(d->create_time) : 0;
  if (creator_id)
    *creator_id = (d->flags & MARS_NWE_FILEINFO_HAS_CREATOR)
                ? GET_BE32(d->creator_id) : 0;
  if (modifier_id)
    *modifier_id = (d->flags & MARS_NWE_FILEINFO_HAS_MODIFIER)
                 ? GET_BE32(d->modifier_id) : 0;
}

#if XATTR_SUPPORT
static int get_file_info_data(char *unixname, MARS_NWE_FILEINFO_XATTR_DATA *d)
{
  ssize_t len;

  init_file_info_data(d);
  if (!unixname || !*unixname)
    return(-1);

  len = getxattr(unixname, MARS_NWE_FILEINFO_XATTR, d, sizeof(*d));
  if ((len != sizeof(*d) && len != MARS_NWE_FILEINFO_XATTR_V1_SIZE) ||
      d->version != MARS_NWE_FILEINFO_VERSION) {
    init_file_info_data(d);
    return(-1);
  }
  return(0);
}
#endif

void mars_nwe_get_file_info(char *unixname,
                            uint16 *create_date,
                            uint16 *create_time,
                            uint32 *creator_id,
                            uint8 *flags)
{
  MARS_NWE_FILEINFO_XATTR_DATA d;

#if XATTR_SUPPORT
  (void)get_file_info_data(unixname, &d);
#else
  (void)unixname;
  init_file_info_data(&d);
#endif
  read_file_info_data(&d, create_date, create_time, creator_id, NULL, flags);
}

void mars_nwe_get_file_modifier_info(char *unixname,
                                     uint32 *modifier_id,
                                     uint8 *flags)
{
  MARS_NWE_FILEINFO_XATTR_DATA d;

#if XATTR_SUPPORT
  (void)get_file_info_data(unixname, &d);
#else
  (void)unixname;
  init_file_info_data(&d);
#endif
  read_file_info_data(&d, NULL, NULL, NULL, modifier_id, flags);
}

int mars_nwe_set_file_info(char *unixname,
                           int set_create_date, uint16 create_date,
                           int set_create_time, uint16 create_time,
                           int set_creator, uint32 creator_id)
{
#if XATTR_SUPPORT
  MARS_NWE_FILEINFO_XATTR_DATA d;

  (void)get_file_info_data(unixname, &d);

  if (set_create_date) {
    U16_TO_16(create_date, d.create_date);
    if (create_date)
      d.flags |= MARS_NWE_FILEINFO_HAS_CREATE_DATE;
    else
      d.flags &= (uint8)~MARS_NWE_FILEINFO_HAS_CREATE_DATE;
  }
  if (set_create_time) {
    U16_TO_16(create_time, d.create_time);
    if (create_time)
      d.flags |= MARS_NWE_FILEINFO_HAS_CREATE_TIME;
    else
      d.flags &= (uint8)~MARS_NWE_FILEINFO_HAS_CREATE_TIME;
  }
  if (set_creator) {
    U32_TO_BE32(creator_id, d.creator_id);
    if (creator_id)
      d.flags |= MARS_NWE_FILEINFO_HAS_CREATOR;
    else
      d.flags &= (uint8)~MARS_NWE_FILEINFO_HAS_CREATOR;
  }

  if (!unixname || !*unixname)
    return(0);

  if (!d.flags) {
    if (removexattr(unixname, MARS_NWE_FILEINFO_XATTR)) {
      int err = errno;
#if defined(ENOATTR)
      if (err != ENODATA && err != ENOATTR)
#else
      if (err != ENODATA)
#endif
        XDPRINTF((5,0,"mars_nwe fileinfo xattr remove ignored for %s errno=%d", unixname, err));
    }
    return(0);
  }

  if (setxattr(unixname, MARS_NWE_FILEINFO_XATTR, &d, sizeof(d), 0)) {
    int err = errno;
    /* Compatibility rule: creator metadata has no portable POSIX field.
     * Keep the NCP call successful and only log the missing persistence.
     */
    XDPRINTF((5,0,"mars_nwe fileinfo xattr ignored for %s errno=%d", unixname, err));
  }
#else
  (void)unixname;
  (void)set_create_date;
  (void)create_date;
  (void)set_create_time;
  (void)create_time;
  (void)set_creator;
  (void)creator_id;
#endif
  return(0);
}


int mars_nwe_set_file_modifier_info(char *unixname,
                                    int set_modifier, uint32 modifier_id)
{
#if XATTR_SUPPORT
  MARS_NWE_FILEINFO_XATTR_DATA d;

  (void)get_file_info_data(unixname, &d);

  if (set_modifier) {
    U32_TO_BE32(modifier_id, d.modifier_id);
    if (modifier_id)
      d.flags |= MARS_NWE_FILEINFO_HAS_MODIFIER;
    else
      d.flags &= (uint8)~MARS_NWE_FILEINFO_HAS_MODIFIER;
  }

  if (!unixname || !*unixname)
    return(0);

  if (!d.flags) {
    if (removexattr(unixname, MARS_NWE_FILEINFO_XATTR)) {
      int err = errno;
#if defined(ENOATTR)
      if (err != ENODATA && err != ENOATTR)
#else
      if (err != ENODATA)
#endif
        XDPRINTF((5,0,"mars_nwe fileinfo xattr remove ignored for %s errno=%d", unixname, err));
    }
    return(0);
  }

  if (setxattr(unixname, MARS_NWE_FILEINFO_XATTR, &d, sizeof(d), 0)) {
    int err = errno;
    /* Compatibility rule: modifier metadata has no portable POSIX field.
     * Keep the NCP call successful and only log the missing persistence.
     */
    XDPRINTF((5,0,"mars_nwe fileinfo xattr ignored for %s errno=%d", unixname, err));
  }
#else
  (void)unixname;
  (void)set_modifier;
  (void)modifier_id;
#endif
  return(0);
}
