/*
 * Copyright (c) 2007 Vreixo Formoso
 * Copyright (c) 2007 Mario Danic
 * Copyright (c) 2009 - 2025 Thomas Schmitt
 * 
 * This file is part of the libisofs project; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License version 2 
 * or later as published by the Free Software Foundation. 
 * See COPYING file for details.
 */

/**
 * This header defines the functions and structures needed to add RockRidge
 * extensions to an ISO image. It also handles AAIP and zisofs extensions.
 * 
 * References:
 * 
 * - SUSP (IEEE 1281).
 * System Use Sharing Protocol, draft standard version 1.12.
 * See ftp://ftp.ymi.com/pub/rockridge/susp112.ps
 * 
 * - RRIP (IEEE 1282)
 * Rock Ridge Interchange Protocol, Draft Standard version 1.12.
 * See ftp://ftp.ymi.com/pub/rockridge/rrip112.ps
 * 
 * - ECMA-119 (ISO-9660)
 * Volume and File Structure of CDROM for Information Interchange. See
 * http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf
 *
 * - AAIP
 * Arbitrary Attribute Interchange Protocol. See doc/susp_aaip_2_0.txt
 *
 * - zisofs
 * Blockwise compression of data file content with transparent read support
 * in the Linux kernel. See doc/zisofs_format.txt
 *
 */

#ifndef LIBISO_ROCKRIDGE_H
#define LIBISO_ROCKRIDGE_H

#include "ecma119.h"

/* For ssize_t */
#include <sys/types.h>
#include <unistd.h>


#define SUSP_SIG(entry, a, b) ((entry->sig[0] == a) && (entry->sig[1] == b))

/**
 * This contains the information about the System Use Fields (SUSP, 4.1), 
 * that will be written in the System Use Areas, both in the ISO directory
 * record System Use field (ECMA-119, 9.1.13) or in a Continuation Area as
 * defined by SUSP.
 */
struct susp_info
{
    /** Number of SUSP fields in the System Use field */
    size_t n_susp_fields;
    uint8_t **susp_fields;

    /** Length of the part of the SUSP area that fits in the dirent. */
    int suf_len;

    /** Length of the part of the SUSP area that will go in a CE area. */
    uint32_t ce_block;
    uint32_t ce_len;

    /* Storage for Continuation Area for a whole directory */
    size_t n_ce_susp_fields;
    uint8_t **ce_susp_fields;

    /* The number of allocated members in ce_susp_fields */
    size_t alloc_ce_susp_fields;

    /* Marks the start index in ce_susp_fields of the current node */
    size_t current_ce_start;

    /* Tells the really written number of Continuation Area payload bytes
       after rrip_write_ce_fields() wrote them to the output stream.
    */
    uint64_t ce_written_len;

};

/* Step to increase allocated size of susp_info.ce_susp_fields */
#define ISO_SUSP_CE_ALLOC_STEP 16

/* Upper limit for use of 7-byte time form: 01 Jan 2150 00:00:00 UTC
   That is six years before the end of the 7-byte form capacity, just to
   allow future software archeologists to widen this time limit to end of
   year 2155 so that any problems with the longer TF can be sorted out
   before it is too late.
*/
#define ISO_RR_SHORT_FORM_TIME_LIMIT 5680281600

/* Lower limit for use of 7-byte time form: 01 Jan 1900 00:00:00 UTC
*/
#define ISO_RR_SHORT_FORM_TIME_START -2208988800

/* SUSP 5.1 */
struct susp_CE {
    uint8_t block[8];
    uint8_t offset[8];
    uint8_t len[8];
};

/* SUSP 5.3 */
struct susp_SP {
    uint8_t be[1];
    uint8_t ef[1];
    uint8_t len_skp[1];
};

/* SUSP 5.5 */
struct susp_ER {
    uint8_t len_id[1];
    uint8_t len_des[1];
    uint8_t len_src[1];
    uint8_t ext_ver[1];
    uint8_t ext_id[1]; /*< up to len_id bytes */
    /* ext_des, ext_src */
};

/** POSIX file attributes (RRIP, 4.1.1) */
struct rr_PX {
    uint8_t mode[8];
    uint8_t links[8];
    uint8_t uid[8];
    uint8_t gid[8];
    uint8_t serial[8];
};

/** Time stamps for a file (RRIP, 4.1.6) */
struct rr_TF {
    uint8_t flags[1];
    uint8_t t_stamps[1];
};

/** Info for character and block device (RRIP, 4.1.2) */
struct rr_PN {
    uint8_t high[8];
    uint8_t low[8];
};

/** Alternate name (RRIP, 4.1.4) */
struct rr_NM {
    uint8_t flags[1];
    uint8_t name[1];
};

/** Link for a relocated directory (RRIP, 4.1.5.1) */
struct rr_CL {
    uint8_t child_loc[8];
};

/** Sim link (RRIP, 4.1.3) */
struct rr_SL {
    uint8_t flags[1];
    uint8_t comps[1];
};


/** Outdated Arbitrary Attribute (AAIP, see doc/susp_aaip_1_0.txt)
 *  It collided with pre-SUSP Apple AA field.
 */
struct aaip_AA {
    uint8_t flags[1];
    uint8_t comps[1];
};

/** Arbitrary Attribute (AAIP, see doc/susp_aaip_2_0.txt) */
struct aaip_AL {
    uint8_t flags[1];
    uint8_t comps[1];
};


/** zisofs entry  (see doc/zisofs_format.txt) */
struct zisofs_ZF {
    uint8_t parameters[1]; /* begins with BP 5 */
};


/**
 * Struct for a SUSP System User Entry (SUSP, 4.1)
 */
struct susp_sys_user_entry
{
    uint8_t sig[2];
    uint8_t len_sue[1];
    uint8_t version[1];
    union {
        struct susp_CE CE;
        struct susp_SP SP;
        struct susp_ER ER;
        struct rr_PX PX;
        struct rr_TF TF;
        struct rr_PN PN;
        struct rr_NM NM;
        struct rr_CL CL;
        struct rr_SL SL;
        struct aaip_AA AA;
        struct aaip_AL AL;
        struct zisofs_ZF ZF;
    } data; /* 5 to 4+len_sue */
};

/**
 * Compute the length needed for write all RR and SUSP entries for a given
 * node.
 * 
 * @param type
 *      0 normal entry, 1 "." entry for that node (it is a dir), 2 ".."
 *      for that node (i.e., it will refer to the parent)
 * @param space
 *      Available space in the System Use Area for the directory record.
 * @param ce
 *      Will be filled with the space needed in a CE
 * @param base_ce
 *      Fill of continuation area by previous nodes of same dir
 * @return
 *      The size needed for the RR entries in the System Use Area
 */
ssize_t rrip_calc_len(Ecma119Image *t, Ecma119Node *n, int type, size_t space,
                      size_t *ce, size_t base_ce);

/**
 * Fill a struct susp_info with the RR/SUSP entries needed for a given
 * node.
 * 
 * @param type
 *      0 normal entry, 1 "." entry for that node (it is a dir), 2 ".."
 *      for that node (i.e., it will refer to the parent)
 * @param space
 *      Available space in the System Use Area for the directory record.
 * @param info
 *      Pointer to the struct susp_info where the entries will be stored.
 *      If some entries need to go to a Continuation Area, they will be added
 *      to the existing ce_susp_fields, and ce_len will be incremented
 *      properly. Please ensure ce_block is initialized properly.
 * @return
 *      1 success, < 0 error
 */
int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type,
                         size_t space, struct susp_info *info);

/**
 * Write the given SUSP fields into buf. Note that Continuation Area
 * fields are not written.
 * If info does not contain any SUSP entry this function just return. 
 * After written, the info susp_fields array will be freed, and the counters
 * updated properly.
 * @return the number of written bytes
 */
int rrip_write_susp_fields(Ecma119Image *t, struct susp_info *info,
                            uint8_t *buf);

/**
 * Write the Continuation Area entries for the given struct susp_info, using
 * the iso_write() function.
 * After written, the ce_susp_fields array will be freed.
 */
int rrip_write_ce_fields(Ecma119Image *t, struct susp_info *info);

/**
 * The SUSP iterator is used to iterate over the System User Entries
 * of a ECMA-168 directory record.
 * It takes care about Continuation Areas, handles the end of the different
 * system user entries and skip padding areas. Thus, using an iteration
 * we are accessing just to the meaning entries.
 */
typedef struct susp_iterator SuspIterator;

SuspIterator *
susp_iter_new(IsoDataSource *src, struct ecma119_dir_record *record, 
              uint32_t fs_blocks, uint8_t len_skp, int msgid);

/**
 * Get the next SUSP System User Entry using given iterator.
 * 
 * @param sue
 *      Pointer to the next susp entry. It refers to an internal buffer and 
 *      it's not guaranteed to be allocated after calling susp_iter_next() 
 *      again. Thus, if you need to keep some entry you have to do a copy.
 * @return
 *      1 on success, 0 if no more entries, < 0 error
 */
int susp_iter_next(SuspIterator *iter, struct susp_sys_user_entry **sue,
                   int flag);

/**
 * Free a given susp iterator.
 */
void susp_iter_free(SuspIterator *iter);


/**
 * Fills a struct stat with the values of a Rock Ridge PX entry (RRIP, 4.1.1).
 * 
 * @return 
 *    < 0 on error
 *      1 on success with no inode number,
 *      2 on success with inode number,
 */
int read_rr_PX(struct susp_sys_user_entry *px, struct stat *st);

/**
 * Fills a struct stat with the values of a Rock Ridge TF entry (RRIP, 4.1.6)
 * 
 * @return 
 *      1 on success, < 0 on error
 */
int read_rr_TF(struct susp_sys_user_entry *tf, struct stat *st);

/**
 * Read a RR NM entry (RRIP, 4.1.4), and appends the name stored there to
 * the given name. You can pass a pointer to NULL as name.
 * 
 * @return
 *      1 on success, < 0 on error
 */
int read_rr_NM(struct susp_sys_user_entry *nm, char **name, int *cont);

/**
 * Read a SL RR entry (RRIP, 4.1.3), checking if the destination continues.
 * 
 * @param cont
 *      0 not continue, 1 continue, 2 continue component 
 * @return
 *      1 on success, < 0 on error
 */
int read_rr_SL(struct susp_sys_user_entry *sl, char **dest, int *cont);

/**
 * Fills a struct stat with the values of a Rock Ridge PN entry (RRIP, 4.1.2).
 * 
 * @return 
 *      1 on success, < 0 on error
 */
int read_rr_PN(struct susp_sys_user_entry *pn, struct stat *st);


/**
 * Collects the AAIP field string from single AAIP fields.
 * (see doc/susp_aaip_1_0.txt)
 * @param aa_string   Storage location of the emerging string.
 *                    Begin with *aa_string == NULL, or own malloc() storage.
 * @param aa_size     Current allocated size of aa_string.
 *                    Begin with *aa_size == 0, or own storage size.
 * @param aa_len      Current occupied size of aa_string.
 *                    Begin with *aa_len == 0
 * @param prev_field  Returns the index of start of the previous field
 *                    in the string.
 * @param is_done     The current completion state of the AAIP field string.
 *                    Fields will be ignored as soon as it is 1.
 *                    Begin with *is_done == 0
 * @param flag        Unused yet. Submit 0.
 * @return 
 *      1 on success, < 0 on error
 */
int read_aaip_AA(struct susp_sys_user_entry *sue,
                 unsigned char **aa_string, size_t *aa_size, size_t *aa_len,
                 size_t *prev_field, int *is_done, int flag);

/**
 * Collects the AAIP field string from single AL fields.
 * (see doc/susp_aaip_2_0.txt)
 */
int read_aaip_AL(struct susp_sys_user_entry *sue,
                 unsigned char **aa_string, size_t *aa_size, size_t *aa_len,
                 size_t *prev_field, int *is_done, int flag);

/**
 * Reads the zisofs parameters from a ZF field (see doc/zisofs_format.txt).
 *
 * @return
 *      1 on success, < 0 on error
 */
int read_zisofs_ZF(struct susp_sys_user_entry *zf, uint8_t algorithm[2],
                   uint8_t *header_size_div4, uint8_t *block_size_log2,
                   uint64_t *uncompressed_size, int flag);

/**
 * Convert a RR filename to the requested charset.
 * @param flag   bit0= do not issue error messages
 */
int iso_get_rr_name(IsoWriteOpts *opts, char *input_charset,
                    char *output_charset, int imgid,
                    char *str, char **name, int flag);

#endif /* LIBISO_ROCKRIDGE_H */
