libisofs/libisofs/rockridge.c

1209 lines
36 KiB
C

/*
* Copyright (c) 2007 Vreixo Formoso
* Copyright (c) 2007 Mario Danic
*
* 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 as
* published by the Free Software Foundation. See COPYING file for details.
*/
#include "rockridge.h"
#include "node.h"
#include "ecma119_tree.h"
#include "writer.h"
#include "messages.h"
#include "image.h"
#include <string.h>
static
int susp_append(Ecma119Image *t, struct susp_info *susp, uint8_t *data)
{
susp->n_susp_fields++;
susp->susp_fields = realloc(susp->susp_fields, sizeof(void*)
* susp->n_susp_fields);
if (susp->susp_fields == NULL) {
return ISO_OUT_OF_MEM;
}
susp->susp_fields[susp->n_susp_fields - 1] = data;
susp->suf_len += data[2];
return ISO_SUCCESS;
}
static
int susp_append_ce(Ecma119Image *t, struct susp_info *susp, uint8_t *data)
{
susp->n_ce_susp_fields++;
susp->ce_susp_fields = realloc(susp->ce_susp_fields, sizeof(void*)
* susp->n_ce_susp_fields);
if (susp->ce_susp_fields == NULL) {
return ISO_OUT_OF_MEM;
}
susp->ce_susp_fields[susp->n_ce_susp_fields - 1] = data;
susp->ce_len += data[2];
return ISO_SUCCESS;
}
static
uid_t px_get_uid(Ecma119Image *t, Ecma119Node *n)
{
if (t->replace_uid) {
return t->uid;
} else {
return n->node->uid;
}
}
static
uid_t px_get_gid(Ecma119Image *t, Ecma119Node *n)
{
if (t->replace_gid) {
return t->gid;
} else {
return n->node->gid;
}
}
static
mode_t px_get_mode(Ecma119Image *t, Ecma119Node *n)
{
if ((n->type == ECMA119_DIR || n->type == ECMA119_PLACEHOLDER)) {
if (t->replace_dir_mode) {
return (n->node->mode & S_IFMT) | t->dir_mode;
}
} else {
if (t->replace_file_mode) {
return (n->node->mode & S_IFMT) | t->file_mode;
}
}
return n->node->mode;
}
/**
* Add a PX System Use Entry. The PX System Use Entry is used to add POSIX
* file attributes, such as access permissions or user and group id, to a
* ECMA 119 directory record. (RRIP, 4.1.1)
*/
static
int rrip_add_PX(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp)
{
uint8_t *PX = malloc(44);
if (PX == NULL) {
return ISO_OUT_OF_MEM;
}
PX[0] = 'P';
PX[1] = 'X';
PX[2] = 44;
PX[3] = 1;
iso_bb(&PX[4], px_get_mode(t, n), 4);
iso_bb(&PX[12], n->nlink, 4);
iso_bb(&PX[20], px_get_uid(t, n), 4);
iso_bb(&PX[28], px_get_gid(t, n), 4);
iso_bb(&PX[36], n->ino, 4);
return susp_append(t, susp, PX);
}
/**
* Add to the given tree node a TF System Use Entry, used to record some
* time stamps related to the file (RRIP, 4.1.6).
*/
static
int rrip_add_TF(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp)
{
IsoNode *iso;
uint8_t *TF = malloc(5 + 3 * 7);
if (TF == NULL) {
return ISO_OUT_OF_MEM;
}
TF[0] = 'T';
TF[1] = 'F';
TF[2] = 5 + 3 * 7;
TF[3] = 1;
TF[4] = (1 << 1) | (1 << 2) | (1 << 3);
iso = n->node;
iso_datetime_7(&TF[5], t->replace_timestamps ? t->timestamp : iso->mtime,
t->always_gmt);
iso_datetime_7(&TF[12], t->replace_timestamps ? t->timestamp : iso->atime,
t->always_gmt);
iso_datetime_7(&TF[19], t->replace_timestamps ? t->timestamp : iso->ctime,
t->always_gmt);
return susp_append(t, susp, TF);
}
/**
* Add a PL System Use Entry, used to record the location of the original
* parent directory of a directory which has been relocated.
*
* This is special because it doesn't modify the susp fields of the directory
* that gets passed to it; it modifies the susp fields of the ".." entry in
* that directory.
*
* See RRIP, 4.1.5.2 for more details.
*/
static
int rrip_add_PL(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp)
{
uint8_t *PL;
if (n->type != ECMA119_DIR || n->info.dir->real_parent == NULL) {
/* should never occur */
return ISO_ASSERT_FAILURE;
}
PL = malloc(12);
if (PL == NULL) {
return ISO_OUT_OF_MEM;
}
PL[0] = 'P';
PL[1] = 'L';
PL[2] = 12;
PL[3] = 1;
/* write the location of the real parent, already computed */
iso_bb(&PL[4], n->info.dir->real_parent->info.dir->block, 4);
return susp_append(t, susp, PL);
}
/**
* Add a RE System Use Entry to the given tree node. The purpose of the
* this System Use Entry is to indicate to an RRIP-compliant receiving
* system that the Directory Record in which an "RE" System Use Entry is
* recorded has been relocated from another position in the original
* Directory Hierarchy.
*
* See RRIP, 4.1.5.3 for more details.
*/
static
int rrip_add_RE(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp)
{
uint8_t *RE = malloc(4);
if (RE == NULL) {
return ISO_OUT_OF_MEM;
}
RE[0] = 'R';
RE[1] = 'E';
RE[2] = 4;
RE[3] = 1;
return susp_append(t, susp, RE);
}
/**
* Add a PN System Use Entry to the given tree node.
* The PN System Use Entry is used to store the device number, and it's
* mandatory if the tree node corresponds to a character or block device.
*
* See RRIP, 4.1.2 for more details.
*/
static
int rrip_add_PN(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp)
{
IsoSpecial *node;
uint8_t *PN;
node = (IsoSpecial*)n->node;
if (node->node.type != LIBISO_SPECIAL) {
/* should never occur */
return ISO_ASSERT_FAILURE;
}
PN = malloc(20);
if (PN == NULL) {
return ISO_OUT_OF_MEM;
}
PN[0] = 'P';
PN[1] = 'N';
PN[2] = 20;
PN[3] = 1;
iso_bb(&PN[4], node->dev >> 32, 4);
iso_bb(&PN[12], node->dev & 0xffffffff, 4);
return susp_append(t, susp, PN);
}
/**
* Add to the given tree node a CL System Use Entry, that is used to record
* the new location of a directory which has been relocated.
*
* See RRIP, 4.1.5.1 for more details.
*/
static
int rrip_add_CL(Ecma119Image *t, Ecma119Node *n, struct susp_info *susp)
{
uint8_t *CL;
if (n->type != ECMA119_PLACEHOLDER) {
/* should never occur */
return ISO_ASSERT_FAILURE;
}
CL = malloc(12);
if (CL == NULL) {
return ISO_OUT_OF_MEM;
}
CL[0] = 'C';
CL[1] = 'L';
CL[2] = 12;
CL[3] = 1;
iso_bb(&CL[4], n->info.real_me->info.dir->block, 4);
return susp_append(t, susp, CL);
}
/**
* Convert a RR filename to the requested charset. On any conversion error,
* the original name will be used.
*/
static
char *get_rr_fname(Ecma119Image *t, const char *str)
{
int ret;
char *name;
if (!strcmp(t->input_charset, t->output_charset)) {
/* no conversion needed */
return strdup(str);
}
ret = strconv(str, t->input_charset, t->output_charset, &name);
if (ret < 0) {
/* TODO we should check for possible cancelation */
iso_msg_submit(t->image->id, ISO_FILENAME_WRONG_CHARSET, ret,
"Charset conversion error. Can't convert %s from %s to %s",
str, t->input_charset, t->output_charset);
/* use the original name, it's the best we can do */
name = strdup(str);
}
return name;
}
/**
* Add a NM System Use Entry to the given tree node. The purpose of this
* System Use Entry is to store the content of an Alternate Name to support
* POSIX-style or other names.
*
* See RRIP, 4.1.4 for more details.
*
* @param size
* Length of the name to be included into the NM
* @param flags
* @param ce
* Whether to add or not to CE
*/
static
int rrip_add_NM(Ecma119Image *t, struct susp_info *susp, char *name, int size,
int flags, int ce)
{
uint8_t *NM = malloc(size + 5);
if (NM == NULL) {
return ISO_OUT_OF_MEM;
}
NM[0] = 'N';
NM[1] = 'M';
NM[2] = size + 5;
NM[3] = 1;
NM[4] = flags;
if (size) {
memcpy(&NM[5], name, size);
}
if (ce) {
return susp_append_ce(t, susp, NM);
} else {
return susp_append(t, susp, NM);
}
}
/**
* Add a new SL component (RRIP, 4.1.3.1) to a list of components.
*
* @param n
* Number of components. It will be updated.
* @param compos
* Pointer to the list of components.
* @param s
* The component content
* @param size
* Size of the component content
* @param fl
* Flags
* @return
* 1 on success, < 0 on error
*/
static
int rrip_SL_append_comp(size_t *n, uint8_t ***comps, char *s, int size, char fl)
{
uint8_t *comp = malloc(size + 2);
if (comp == NULL) {
return ISO_OUT_OF_MEM;
}
(*n)++;
comp[0] = fl;
comp[1] = size;
*comps = realloc(*comps, (*n) * sizeof(void*));
if (*comps == NULL) {
free(comp);
return ISO_OUT_OF_MEM;
}
(*comps)[(*n) - 1] = comp;
if (size) {
memcpy(&comp[2], s, size);
}
return ISO_SUCCESS;
}
/**
* Add a SL System Use Entry to the given tree node. This is used to store
* the content of a symbolic link, and is mandatory if the tree node
* indicates a symbolic link (RRIP, 4.1.3).
*
* @param comp
* Components of the SL System Use Entry. If they don't fit in a single
* SL, more than one SL will be added.
* @param n
* Number of components in comp
* @param ce
* Whether to add to a continuation area or system use field.
*/
static
int rrip_add_SL(Ecma119Image *t, struct susp_info *susp, uint8_t **comp,
size_t n, int ce)
{
int ret, i, j;
int total_comp_len = 0;
size_t pos, written = 0;
uint8_t *SL;
for (i = 0; i < n; i++) {
total_comp_len += comp[i][1] + 2;
if (total_comp_len > 250) {
/* we need a new SL entry */
total_comp_len -= comp[i][1] + 2;
SL = malloc(total_comp_len + 5);
if (SL == NULL) {
return ISO_OUT_OF_MEM;
}
SL[0] = 'S';
SL[1] = 'L';
SL[2] = total_comp_len + 5;
SL[3] = 1;
SL[4] = 1; /* CONTINUE */
pos = 5;
for (j = written; j < i; j++) {
memcpy(&SL[pos], comp[j], comp[j][1] + 2);
pos += comp[j][1] + 2;
}
/*
* In this case we are sure we're writting to CE. Check for
* debug purposes
*/
if (ce == 0) {
return ISO_ASSERT_FAILURE;
}
ret = susp_append_ce(t, susp, SL);
if (ret < 0) {
return ret;
}
written = i;
total_comp_len = comp[i][1] + 2;
}
}
SL = malloc(total_comp_len + 5);
if (SL == NULL) {
return ISO_OUT_OF_MEM;
}
SL[0] = 'S';
SL[1] = 'L';
SL[2] = total_comp_len + 5;
SL[3] = 1;
SL[4] = 0;
pos = 5;
for (j = written; j < n; j++) {
memcpy(&SL[pos], comp[j], comp[j][1] + 2);
pos += comp[j][1] + 2;
}
if (ce) {
ret = susp_append_ce(t, susp, SL);
} else {
ret = susp_append(t, susp, SL);
}
return ret;
}
/**
* Add a SUSP "ER" System Use Entry to identify the Rock Ridge specification.
*
* The "ER" System Use Entry is used to uniquely identify a specification
* compliant with SUSP. This method adds to the given tree node "." entry
* the "ER" corresponding to the RR protocol.
*
* See SUSP, 5.5 and RRIP, 4.3 for more details.
*/
static
int rrip_add_ER(Ecma119Image *t, struct susp_info *susp)
{
unsigned char *ER = malloc(182);
if (ER == NULL) {
return ISO_OUT_OF_MEM;
}
ER[0] = 'E';
ER[1] = 'R';
ER[2] = 182;
ER[3] = 1;
ER[4] = 9;
ER[5] = 72;
ER[6] = 93;
ER[7] = 1;
memcpy(&ER[8], "IEEE_1282", 9);
memcpy(&ER[17], "THE IEEE 1282 PROTOCOL PROVIDES SUPPORT FOR POSIX "
"FILE SYSTEM SEMANTICS.", 72);
memcpy(&ER[89], "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, "
"PISCATAWAY, NJ, USA FOR THE 1282 SPECIFICATION.", 93);
/** This always goes to continuation area */
return susp_append_ce(t, susp, ER);
}
/**
* Add a CE System Use Entry to the given tree node. A "CE" is used to add
* a continuation area, where additional System Use Entry can be written.
* (SUSP, 5.1).
*/
static
int susp_add_CE(Ecma119Image *t, size_t ce_len, struct susp_info *susp)
{
uint8_t *CE = malloc(28);
if (CE == NULL) {
return ISO_OUT_OF_MEM;
}
CE[0] = 'C';
CE[1] = 'E';
CE[2] = 28;
CE[3] = 1;
iso_bb(&CE[4], susp->ce_block, 4);
iso_bb(&CE[12], susp->ce_len, 4);
iso_bb(&CE[20], ce_len, 4);
return susp_append(t, susp, CE);
}
/**
* Add a SP System Use Entry. The SP provide an identifier that the SUSP is
* used within the volume. The SP shall be recorded in the "." entry of the
* root directory. See SUSP, 5.3 for more details.
*/
static
int susp_add_SP(Ecma119Image *t, struct susp_info *susp)
{
unsigned char *SP = malloc(7);
if (SP == NULL) {
return ISO_OUT_OF_MEM;
}
SP[0] = 'S';
SP[1] = 'P';
SP[2] = (char)7;
SP[3] = (char)1;
SP[4] = 0xbe;
SP[5] = 0xef;
SP[6] = 0;
return susp_append(t, susp, SP);
}
/**
* 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
* @return
* The size needed for the RR entries in the System Use Area
*/
size_t rrip_calc_len(Ecma119Image *t, Ecma119Node *n, int type, size_t space,
size_t *ce)
{
size_t su_size;
/* space min is 255 - 33 - 37 = 185
* At the same time, it is always an odd number, but we need to pad it
* propertly to ensure the length of a directory record is a even number
* (ECMA-119, 9.1.13). Thus, in fact the real space is always space - 1
*/
space--;
*ce = 0;
/* PX and TF, we are sure they always fit in SUA */
su_size = 44 + 26;
if (n->type == ECMA119_DIR) {
if (n->info.dir->real_parent != NULL) {
/* it is a reallocated entry */
if (type == 2) {
/* we need to add a PL entry */
su_size += 12;
} else if (type == 0) {
/* we need to add a RE entry */
su_size += 4;
}
}
} else if (n->type == ECMA119_SPECIAL) {
if (S_ISBLK(n->node->mode) || S_ISCHR(n->node->mode)) {
/* block or char device, we need a PN entry */
su_size += 20;
}
} else if (n->type == ECMA119_PLACEHOLDER) {
/* we need the CL entry */
su_size += 12;
}
if (type == 0) {
char *name = get_rr_fname(t, n->node->name);
size_t namelen = strlen(name);
free(name);
/* NM entry */
if (su_size + 5 + namelen <= space) {
/* ok, it fits in System Use Area */
su_size += 5 + namelen;
} else {
/* the NM will be divided in a CE */
namelen = namelen - (space - su_size - 5 - 28);
*ce = 5 + namelen;
su_size = space;
}
if (n->type == ECMA119_SYMLINK) {
/*
* for symlinks, we also need to write the SL
*/
char *dest, *cur, *prev;
size_t sl_len = 5;
int cew = (*ce != 0); /* are we writing to CE? */
dest = get_rr_fname(t, ((IsoSymlink*)n->node)->dest);
prev = dest;
cur = strchr(prev, '/');
while (1) {
size_t clen;
if (cur) {
clen = cur - prev;
} else {
/* last component */
clen = strlen(prev);
}
if (clen == 1 && prev[0] == '.') {
clen = 0;
} else if (clen == 2 && prev[0] == '.' && prev[1] == '.') {
clen = 0;
}
/* flags and len for each component record (RRIP, 4.1.3.1) */
clen += 2;
if (!cew) {
/* we are still writing to the SUA */
if (su_size + sl_len + clen > space) {
/*
* ok, we need a Continuation Area anyway
* TODO this can be handled better, but for now SL
* will be completelly moved into the CA
*/
if (su_size + 28 <= space) {
/* the CE entry fills without reducing NM */
su_size += 28;
} else {
/* we need to reduce NM */
*ce = (28 - (space - su_size)) + 5;
su_size = space;
}
cew = 1;
} else {
sl_len += clen;
}
}
if (cew) {
if (sl_len + clen > 255) {
/* we need an additional SL entry */
if (clen > 250) {
/*
* case 1, component too large to fit in a
* single SL entry. Thus, the component need
* to be divided anyway.
* Note than clen can be up to 255 + 2 = 257.
*
* First, we check how many bytes fit in current
* SL field
*/
int fit = 255 - sl_len - 2;
if (clen - 250 <= fit) {
/*
* the component can be divided between this
* and another SL entry
*/
*ce += 255; /* this SL, full */
sl_len = 5 + (clen - fit);
} else {
/*
* the component will need a 2rd SL entry in
* any case, so we prefer to don't write
* anything in this SL
*/
*ce += sl_len + 255;
sl_len = 5 + (clen - 250) + 2;
}
} else {
/* case 2, create a new SL entry */
*ce += sl_len;
sl_len = 5 + clen;
}
} else {
sl_len += clen;
}
}
if (!cur || cur[1] == '\0') {
/* cur[1] can be \0 if dest ends with '/' */
break;
}
prev = cur + 1;
cur = strchr(prev, '/');
}
free(dest);
/* and finally write the pending SL field */
if (!cew) {
/* the whole SL fits into the SUA */
su_size += sl_len;
} else {
*ce += sl_len;
}
}
} else {
/* "." or ".." entry */
su_size += 5; /* NM field */
if (type == 1 && n->parent == NULL) {
/*
* "." for root directory
* we need to write SP and ER entries. The first fits in SUA,
* ER needs a Continuation Area, thus we also need a CE entry
*/
su_size += 7 + 28; /* SP + CE */
*ce = 182; /* ER */
}
}
/*
* The System Use field inside the directory record must be padded if
* it is an odd number (ECMA-119, 9.1.13)
*/
su_size += (su_size % 2);
return su_size;
}
/**
* Free all info in a struct susp_info.
*/
static
void susp_info_free(struct susp_info* susp)
{
size_t i;
for (i = 0; i < susp->n_susp_fields; ++i) {
free(susp->susp_fields[i]);
}
free(susp->susp_fields);
for (i = 0; i < susp->n_ce_susp_fields; ++i) {
free(susp->ce_susp_fields[i]);
}
free(susp->ce_susp_fields);
}
/**
* 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
* propertly. Please ensure ce_block is initialized propertly.
* @return
* 1 success, < 0 error
*/
int rrip_get_susp_fields(Ecma119Image *t, Ecma119Node *n, int type,
size_t space, struct susp_info *info)
{
int ret;
size_t i;
Ecma119Node *node;
char *name = NULL;
char *dest = NULL;
if (t == NULL || n == NULL || info == NULL) {
return ISO_NULL_POINTER;
}
if (type < 0 || type > 2 || space < 185) {
/* space min is 255 - 33 - 37 = 185 */
return ISO_WRONG_ARG_VALUE;
}
if (type == 2 && n->parent != NULL) {
node = n->parent;
} else {
node = n;
}
/* space min is 255 - 33 - 37 = 185
* At the same time, it is always an odd number, but we need to pad it
* propertly to ensure the length of a directory record is a even number
* (ECMA-119, 9.1.13). Thus, in fact the real space is always space - 1
*/
space--;
/*
* SP must be the first entry for the "." record of the root directory
* (SUSP, 5.3)
*/
if (type == 1 && n->parent == NULL) {
ret = susp_add_SP(t, info);
if (ret < 0) {
goto add_susp_cleanup;
}
}
/* PX and TF, we are sure they always fit in SUA */
ret = rrip_add_PX(t, node, info);
if (ret < 0) {
goto add_susp_cleanup;
}
ret = rrip_add_TF(t, node, info);
if (ret < 0) {
goto add_susp_cleanup;
}
if (n->type == ECMA119_DIR) {
if (n->info.dir->real_parent != NULL) {
/* it is a reallocated entry */
if (type == 2) {
/*
* we need to add a PL entry
* Note that we pass "n" as parameter, not "node"
*/
ret = rrip_add_PL(t, n, info);
if (ret < 0) {
goto add_susp_cleanup;
}
} else if (type == 0) {
/* we need to add a RE entry */
ret = rrip_add_RE(t, node, info);
if (ret < 0) {
goto add_susp_cleanup;
}
}
}
} else if (n->type == ECMA119_SPECIAL) {
if (S_ISBLK(n->node->mode) || S_ISCHR(n->node->mode)) {
/* block or char device, we need a PN entry */
ret = rrip_add_PN(t, node, info);
if (ret < 0) {
goto add_susp_cleanup;
}
}
} else if (n->type == ECMA119_PLACEHOLDER) {
/* we need the CL entry */
ret = rrip_add_CL(t, node, info);
if (ret < 0) {
goto add_susp_cleanup;
}
}
if (type == 0) {
size_t sua_free; /* free space in the SUA */
int nm_type = 0; /* 0 whole entry in SUA, 1 part in CE */
size_t ce_len = 0; /* len of the CE */
size_t namelen;
/* this two are only defined for symlinks */
uint8_t **comps= NULL; /* components of the SL field */
size_t n_comp = 0; /* number of components */
name = get_rr_fname(t, n->node->name);
namelen = strlen(name);
sua_free = space - info->suf_len;
/* NM entry */
if (5 + namelen <= sua_free) {
/* ok, it fits in System Use Area */
sua_free -= (5 + namelen);
nm_type = 0;
} else {
/* the NM will be divided in a CE */
nm_type = 1;
namelen = namelen - (sua_free - 5 - 28);
ce_len = 5 + namelen;
sua_free = 0;
}
if (n->type == ECMA119_SYMLINK) {
/*
* for symlinks, we also need to write the SL
*/
char *cur, *prev;
size_t sl_len = 5;
int cew = (nm_type == 1); /* are we writing to CE? */
dest = get_rr_fname(t, ((IsoSymlink*)n->node)->dest);
prev = dest;
cur = strchr(prev, '/');
while (1) {
size_t clen;
char cflag = 0; /* component flag (RRIP, 4.1.3.1) */
if (cur) {
clen = cur - prev;
} else {
/* last component */
clen = strlen(prev);
}
if (clen == 0) {
/* this refers to the roor directory, '/' */
cflag = 1 << 3;
}
if (clen == 1 && prev[0] == '.') {
clen = 0;
cflag = 1 << 1;
} else if (clen == 2 && prev[0] == '.' && prev[1] == '.') {
clen = 0;
cflag = 1 << 2;
}
/* flags and len for each component record (RRIP, 4.1.3.1) */
clen += 2;
if (!cew) {
/* we are still writing to the SUA */
if (sl_len + clen > sua_free) {
/*
* ok, we need a Continuation Area anyway
* TODO this can be handled better, but for now SL
* will be completelly moved into the CA
*/
if (28 <= sua_free) {
/* the CE entry fills without reducing NM */
sua_free -= 28;
cew = 1;
} else {
/* we need to reduce NM */
nm_type = 1;
ce_len = (28 - sua_free) + 5;
sua_free = 0;
cew = 1;
}
} else {
/* add the component */
ret = rrip_SL_append_comp(&n_comp, &comps, prev,
clen - 2, cflag);
if (ret < 0) {
goto add_susp_cleanup;
}
sl_len += clen;
}
}
if (cew) {
if (sl_len + clen > 255) {
/* we need an addition SL entry */
if (clen > 250) {
/*
* case 1, component too large to fit in a
* single SL entry. Thus, the component need
* to be divided anyway.
* Note than clen can be up to 255 + 2 = 257.
*
* First, we check how many bytes fit in current
* SL field
*/
int fit = 255 - sl_len - 2;
if (clen - 250 <= fit) {
/*
* the component can be divided between this
* and another SL entry
*/
ret = rrip_SL_append_comp(&n_comp, &comps,
prev, fit, 0x01);
if (ret < 0) {
goto add_susp_cleanup;
}
/*
* and another component, that will go in
* other SL entry
*/
ret = rrip_SL_append_comp(&n_comp, &comps, prev
+ fit, clen - fit - 2, 0);
if (ret < 0) {
goto add_susp_cleanup;
}
ce_len += 255; /* this SL, full */
sl_len = 5 + (clen - fit);
} else {
/*
* the component will need a 2rd SL entry in
* any case, so we prefer to don't write
* anything in this SL
*/
ret = rrip_SL_append_comp(&n_comp, &comps,
prev, 248, 0x01);
if (ret < 0) {
goto add_susp_cleanup;
}
ret = rrip_SL_append_comp(&n_comp, &comps, prev
+ 248, strlen(prev + 248), 0x00);
if (ret < 0) {
goto add_susp_cleanup;
}
ce_len += sl_len + 255;
sl_len = 5 + (clen - 250) + 2;
}
} else {
/* case 2, create a new SL entry */
ret = rrip_SL_append_comp(&n_comp, &comps, prev,
clen - 2, cflag);
if (ret < 0) {
goto add_susp_cleanup;
}
ce_len += sl_len;
sl_len = 5 + clen;
}
} else {
/* the component fit in the SL entry */
ret = rrip_SL_append_comp(&n_comp, &comps, prev,
clen - 2, cflag);
if (ret < 0) {
goto add_susp_cleanup;
}
sl_len += clen;
}
}
if (!cur || cur[1] == '\0') {
/* cur[1] can be \0 if dest ends with '/' */
break;
}
prev = cur + 1;
cur = strchr(prev, '/');
}
if (cew) {
ce_len += sl_len;
}
}
/*
* We we reach here:
* - We know if NM fill in the SUA (nm_type == 0)
* - If SL needs an to be written in CE (ce_len > 0)
* - The components for SL entry (or entries)
*/
if (nm_type == 0) {
/* the full NM fills in SUA */
ret = rrip_add_NM(t, info, name, strlen(name), 0, 0);
if (ret < 0) {
goto add_susp_cleanup;
}
} else {
/*
* Write the NM part that fits in SUA... Note that CE
* entry and NM in the continuation area is added below
*/
namelen = space - info->suf_len - 28 - 5;
ret = rrip_add_NM(t, info, name, namelen, 1, 0);
if (ret < 0) {
goto add_susp_cleanup;
}
}
if (ce_len > 0) {
/* Add the CE entry */
ret = susp_add_CE(t, ce_len, info);
if (ret < 0) {
goto add_susp_cleanup;
}
}
if (nm_type == 1) {
/*
* ..and the part that goes to continuation area.
*/
ret = rrip_add_NM(t, info, name + namelen, strlen(name + namelen),
0, 1);
if (ret < 0) {
goto add_susp_cleanup;
}
}
if (n->type == ECMA119_SYMLINK) {
/* add the SL entry (or entries) */
ret = rrip_add_SL(t, info, comps, n_comp, (ce_len > 0));
/* free the components */
for (i = 0; i < n_comp; i++) {
free(comps[i]);
}
free(comps);
if (ret < 0) {
goto add_susp_cleanup;
}
}
} else {
/* "." or ".." entry */
/* write the NM entry */
ret = rrip_add_NM(t, info, NULL, 0, 1 << type, 0);
if (ret < 0) {
goto add_susp_cleanup;
}
if (type == 1 && n->parent == NULL) {
/*
* "." for root directory
* we need to write SP and ER entries. The first fits in SUA,
* ER needs a Continuation Area, thus we also need a CE entry.
* Note that SP entry was already added above
*/
ret = susp_add_CE(t, 182, info); /* 182 is ER length */
if (ret < 0) {
goto add_susp_cleanup;
}
ret = rrip_add_ER(t, info);
if (ret < 0) {
goto add_susp_cleanup;
}
}
}
/*
* The System Use field inside the directory record must be padded if
* it is an odd number (ECMA-119, 9.1.13)
*/
info->suf_len += (info->suf_len % 2);
free(name);
free(dest);
return ISO_SUCCESS;
add_susp_cleanup: ;
free(name);
free(dest);
susp_info_free(info);
return ret;
}
/**
* 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 propertly.
*/
void rrip_write_susp_fields(Ecma119Image *t, struct susp_info *info,
uint8_t *buf)
{
size_t i;
size_t pos = 0;
if (info->n_susp_fields == 0) {
return;
}
for (i = 0; i < info->n_susp_fields; i++) {
memcpy(buf + pos, info->susp_fields[i], info->susp_fields[i][2]);
pos += info->susp_fields[i][2];
}
/* free susp_fields */
for (i = 0; i < info->n_susp_fields; ++i) {
free(info->susp_fields[i]);
}
free(info->susp_fields);
info->susp_fields = NULL;
info->n_susp_fields = 0;
info->suf_len = 0;
}
/**
* 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)
{
size_t i;
uint8_t padding[BLOCK_SIZE];
int ret= ISO_SUCCESS;
if (info->n_ce_susp_fields == 0) {
return ret;
}
for (i = 0; i < info->n_ce_susp_fields; i++) {
ret = iso_write(t, info->ce_susp_fields[i],
info->ce_susp_fields[i][2]);
if (ret < 0) {
goto write_ce_field_cleanup;
}
}
/* pad continuation area until block size */
i = BLOCK_SIZE - (info->ce_len % BLOCK_SIZE);
if (i > 0 && i < BLOCK_SIZE) {
memset(padding, 0, i);
ret = iso_write(t, padding, i);
}
write_ce_field_cleanup: ;
/* free ce_susp_fields */
for (i = 0; i < info->n_ce_susp_fields; ++i) {
free(info->ce_susp_fields[i]);
}
free(info->ce_susp_fields);
info->ce_susp_fields = NULL;
info->n_ce_susp_fields = 0;
info->ce_len = 0;
return ret;
}