/* BLURB gpl

                           Coda File System
                              Release 6

          Copyright (c) 1987-2016 Carnegie Mellon University
                  Additional copyrights listed below

This  code  is  distributed "AS IS" without warranty of any kind under
the terms of the GNU General Public Licence Version 2, as shown in the
file  LICENSE.  The  technical and financial  contributors to Coda are
listed in the file CREDITS.

                        Additional copyrights
                           none currently

#*/

#ifdef __cplusplus
extern "C" {
#endif

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdarg.h>
#include "coda_string.h"
#include <rpc2/rpc2.h>
#ifdef __cplusplus
}
#endif

#include <util.h>
#include <rec_dlist.h>
#include <vice.h>
#include <srv.h>
#include <rvmlib.h>
#include <vlist.h>
#include "ops.h"
#include "recle.h"
#include "rsle.h"

recle::recle()
{
    abort();
}

recle::~recle()
{
    abort();
}

void recle::InitFromsle(rsle *sl)
{
    LogMsg(9, SrvDebugLevel, stdout, "Entering recle::InitFromSle()\n");
    LogMsg(9, SrvDebugLevel, stdout, "Opcode is %s\n", PRINTOPCODE(sl->opcode));

    RVMLIB_REC_OBJECT(*this);
    rec_dlink *dl = (rec_dlink *)this;
    dl->Init();
    index    = sl->index;
    storeid  = sl->storeid;
    seqno    = sl->seqno;
    opcode   = sl->opcode;
    dvnode   = sl->dvn;
    dunique  = sl->du;
    serverid = ThisHostAddr;

    /* allocate and initialize the variable length part */
    create_rle *c;
    symlink_rle *s;
    link_rle *l;
    mkdir_rle *mk;
    rm_rle *rm;
    rmdir_rle *rmd;
    rename_rle *mvle;
    setquota_rle *sq;
    switch (opcode) {
    case ResolveViceNewStore_OP:
    case RES_NewStore_OP:
        if (sl->u.acl.type == ACLSTORE) {
            size = sizeof(aclstore);
            vle  = new (size) recvarl(size);
            CODA_ASSERT(vle);
            rvmlib_set_range(vle, vle->size());
            aclstore *acls = (aclstore *)(&(vle->vfld[0]));
            acls->init(sl->u.acl.acl);
        } else {
            size = sizeof(newstore);
            vle  = new (size) recvarl(size);
            CODA_ASSERT(vle);
            rvmlib_set_range(vle, vle->size());
            newstore *newst = (newstore *)(&(vle->vfld[0]));
            newst->init(sl->u.newst.owner, sl->u.newst.mode, sl->u.newst.author,
                        sl->u.newst.mtime, sl->u.newst.mask, &sl->u.st.vv);
        }
        break;

    case ResolveViceCreate_OP:
    case RES_Create_OP:
        size = sizeof(create_rle) +
               strlen(sl->name1); // size of information; vle is 4 bytes more
        vle = new (size) recvarl(size);
        CODA_ASSERT(vle);
        rvmlib_set_range(vle, vle->size());
        c = (create_rle *)(&(vle->vfld[0]));
        c->init(sl->u.create.cvnode, sl->u.create.cunique, sl->u.create.owner,
                sl->name1);
        break;
    case ResolveViceSymLink_OP:
    case RES_SymLink_OP:
        size = sizeof(symlink_rle) +
               strlen(sl->name1); // size of information; vle is 4 bytes more
        vle = new (size) recvarl(size);
        CODA_ASSERT(vle);
        rvmlib_set_range(vle, vle->size());
        s = (symlink_rle *)(&(vle->vfld[0]));
        s->init(sl->u.slink.cvnode, sl->u.slink.cunique, sl->u.slink.owner,
                sl->name1);
        break;
    case ResolveViceLink_OP:
    case RES_Link_OP:
        size = sizeof(link_rle) +
               strlen(sl->name1); // size of information; vle is 4 bytes more
        vle = new (size) recvarl(size);
        CODA_ASSERT(vle);
        rvmlib_set_range(vle, vle->size());
        l = (link_rle *)(&(vle->vfld[0]));
        l->init(sl->u.link.cvnode, sl->u.link.cunique, &sl->u.link.cvv,
                sl->name1);
        break;
    case ResolveViceMakeDir_OP:
    case RES_MakeDir_OP:
        size = sizeof(mkdir_rle) +
               strlen(sl->name1); // size of information; vle is 4 bytes more
        vle = new (size) recvarl(size);
        CODA_ASSERT(vle);
        rvmlib_set_range(vle, vle->size());
        mk = (mkdir_rle *)(&(vle->vfld[0]));
        mk->init(sl->u.mkdir.cvnode, sl->u.mkdir.cunique, sl->u.mkdir.owner,
                 sl->name1);
        break;
    case ResolveViceRemove_OP:
    case RES_Remove_OP:
        size = sizeof(rm_rle) +
               strlen(sl->name1); // size of information; vle is 4 bytes more
        vle = new (size) recvarl(size);
        CODA_ASSERT(vle);
        rvmlib_set_range(vle, vle->size());
        rm = (rm_rle *)(&(vle->vfld[0]));
        rm->init(sl->u.rm.cvnode, sl->u.rm.cunique, &sl->u.rm.cvv, sl->name1);
        break;
    case ResolveViceRemoveDir_OP:
    case RES_RemoveDir_OP:
        size = sizeof(rmdir_rle) +
               strlen(sl->name1); // size of information; vle is 4 bytes more
        vle = new (size) recvarl(size);
        CODA_ASSERT(vle);
        rvmlib_set_range(vle, vle->size());
        rmd = (rmdir_rle *)(&(vle->vfld[0]));
        rmd->init(sl->u.rmdir.cvnode, sl->u.rmdir.cunique,
                  sl->u.rmdir.childlist, &sl->u.rmdir.childLCP,
                  &sl->u.rmdir.csid, sl->name1);
        break;
    case ResolveViceRename_OP:
    case RES_Rename_OP:
        size = sizeof(rename_rle) + strlen(sl->name1) + strlen(sl->name2) + 2;
        vle  = new (size) recvarl(size);
        CODA_ASSERT(vle);
        rvmlib_set_range(vle, vle->size());
        mvle = (rename_rle *)(&(vle->vfld[0]));
        mvle->init(sl->u.mv.type, sl->u.mv.otherdirv, sl->u.mv.otherdiru,
                   sl->u.mv.svnode, sl->u.mv.sunique, &sl->u.mv.svv, sl->name1,
                   sl->name2, sl->u.mv.tvnode, sl->u.mv.tunique, &sl->u.mv.tvv,
                   sl->u.mv.tlist);
        break;
    case ResolveViceSetVolumeStatus_OP:
    case RES_SetVolumeStatus_OP:
        size = sizeof(setquota_rle);
        vle  = new (size) recvarl(size);
        CODA_ASSERT(vle);
        rvmlib_set_range(vle, vle->size());
        sq = (setquota_rle *)(&(vle->vfld[0]));
        sq->init(sl->u.sq.oldquota, sl->u.sq.newquota);
        break;
    case ResolveNULL_OP:
    case RES_Repair_OP:
        size = 0;
        vle  = NULL;
        break;
    default:
        LogMsg(0, SrvDebugLevel, stdout, "InitFromsle: unknown opcode %d\n",
               opcode);
        break;
    }
    LogMsg(9, SrvDebugLevel, stdout, "Leaving InitFromSle\n");
}

int recle::FreeVarl()
{
    LogMsg(9, SrvDebugLevel, stdout, "Entering FreeVarl() 0x%x.%x %s\n", dvnode,
           dunique, PRINTOPCODE(opcode));
    if (vle) {
        rvmlib_set_range(&vle, sizeof(recvarl *));
        vle->destroy();
        vle = NULL;
    }
    LogMsg(9, SrvDebugLevel, stdout, "Leaving FreeVarl()\n");
    return (0);
}

/* return pointer to list if this log entry has a pointer to a list of log entries */
rec_dlist *recle::HasList()
{
    if ((opcode == RES_RemoveDir_OP) || (opcode == ResolveViceRemoveDir_OP)) {
        rmdir_rle *r = (rmdir_rle *)(&(vle->vfld[0]));
        return (r->childlist);
    } else if ((opcode == RES_Rename_OP) || (opcode == ResolveViceRename_OP)) {
        rename_rle *mv = (rename_rle *)(&(vle->vfld[0]));
        return (mv->tlist);
    } else
        return NULL;
}

/* recle entries are dumped to a char buffer for shipping
   them to remote sites for resolution.
   Each entry is dumped as follows:
      .
      .
      .
   DUMP_ENTRY_BEGIN_STAMP
   length of this entry
   the fixed length part
   the variable length part
   DUMP_ENTRY_END_STAMP
      .
      .
      .
*/

//return size of buffer to which this recle can be dumped
int recle::GetDumpSize()
{
    /* fixed length part */
    int dumpsize = (int)(sizeof(recle) + (3 * sizeof(long)));
    /* word align dumpsize */
    while (dumpsize % sizeof(long))
        dumpsize++;

    /* variable length part */
    dumpsize += size;
    while (dumpsize % sizeof(long))
        dumpsize++;

    return (dumpsize);
}

char *recle::DumpToBuf(int *bufsize)
{
    *bufsize  = GetDumpSize();
    char *buf = new char[*bufsize];
    CODA_ASSERT(buf);
    memset(buf, 0, *bufsize);
    long *l        = (long *)buf;
    long *lastlong = (long *)(buf + *bufsize - sizeof(long));
    l[0]           = DUMP_ENTRY_BEGIN_STAMP;
    l[1]           = *bufsize;
    char *rlep     = &(buf[2 * sizeof(long)]);
    /* copy the fixed length part */
    memcpy(rlep, this, sizeof(recle));
    rlep += (int)sizeof(recle);

    /* word align the variable length part */
    while (((long)rlep) % sizeof(long))
        rlep++;

    CODA_ASSERT((long)(rlep + size) <= (long)lastlong);
    memcpy(rlep, &(vle->vfld[0]), size);

    *lastlong = DUMP_ENTRY_END_STAMP;
    return (buf);
}

void recle::print()
{
    print(stdout);
}

void recle::print(FILE *fp)
{
    fflush(fp);
    print(fileno(fp));
}

void recle::print(int fd)
{
    char buf[512];
    sprintf(buf, "    **Server: %x StoreId: %x.%x \n", serverid, storeid.HostId,
            storeid.Uniquifier);
    write(fd, buf, (int)strlen(buf));

    sprintf(buf, "    Directory(%08x.%08x)\n    Opcode: %s \n", dvnode, dunique,
            PRINTOPCODE(opcode));
    write(fd, buf, (int)strlen(buf));
    sprintf(buf, "    index is %u, sequence number %d, var length is %d\n",
            index, seqno, size);
    write(fd, buf, (int)strlen(buf));

    /* Decls that used to be inside switch{} but make C++ 3.0 unhappy */
    aclstore *acls;
    newstore *newsp;
    rm_rle *rm;
    create_rle *c;
    rename_rle *mv;
    symlink_rle *s;
    link_rle *l;
    mkdir_rle *mkdir;
    rmdir_rle *rmdir;
    setquota_rle *sq;

    if (vle == NULL) {
        sprintf(buf, "!!!!!!!! vle == NULL pointer !!!!!!\n");
        write(fd, buf, (int)strlen(buf));
        goto Exit;
    }

    switch (opcode) {
    case ResolveViceNewStore_OP:
    case RES_NewStore_OP:
        acls = (aclstore *)&(vle->vfld[0]);
        if (acls->type == ACLSTORE)
            acls->print(fd);
        else {
            newsp = (newstore *)&(vle->vfld[0]);
            CODA_ASSERT(newsp->type == STSTORE);
            newsp->print(fd);
        }
        break;
    case ResolveViceRemove_OP:
    case RES_Remove_OP:
        rm = (rm_rle *)&(vle->vfld[0]);
        rm->print(fd);
        break;
    case ResolveViceCreate_OP:
    case RES_Create_OP:
        c = (create_rle *)&(vle->vfld[0]);
        c->print(fd);
        break;
    case ResolveViceRename_OP:
    case RES_Rename_OP:
        mv = (rename_rle *)&(vle->vfld[0]);
        mv->print(fd);
        break;
    case ResolveViceSymLink_OP:
    case RES_SymLink_OP:
        s = (symlink_rle *)&(vle->vfld[0]);
        s->print(fd);
        break;
    case ResolveViceLink_OP:
    case RES_Link_OP:
        l = (link_rle *)&(vle->vfld[0]);
        l->print(fd);
        break;
    case ResolveViceMakeDir_OP:
    case RES_MakeDir_OP:
        mkdir = (mkdir_rle *)&(vle->vfld[0]);
        mkdir->print(fd);
        break;
    case ResolveViceRemoveDir_OP:
    case RES_RemoveDir_OP:
        rmdir = (rmdir_rle *)&(vle->vfld[0]);
        rmdir->print(fd);
        break;
    case ResolveViceSetVolumeStatus_OP:
    case RES_SetVolumeStatus_OP:
        sq = (setquota_rle *)&(vle->vfld[0]);
        sq->print(fd);
        break;
    case ResolveNULL_OP:
        sprintf(buf, "    ResolveNULL record\n");
        write(fd, buf, (int)strlen(buf));
        break;
    case RES_Repair_OP:
        sprintf(buf, "    ViceRepair record\n");
        write(fd, buf, (int)strlen(buf));
        break;
    default:
        sprintf(buf, "!!!!!!!! Unknown Opcode - record not parsed !!!!!!\n");
        write(fd, buf, (int)strlen(buf));
        break;
    }

Exit:
    sprintf(buf, "    ** End of Record **\n\n");
    write(fd, buf, (int)strlen(buf));
}
