3033 lines
74 KiB
C
3033 lines
74 KiB
C
/* @(#)write.c 1.146 16/12/13 joerg */
|
|
#include <schily/mconfig.h>
|
|
#ifndef lint
|
|
static UConst char sccsid[] =
|
|
"@(#)write.c 1.146 16/12/13 joerg";
|
|
#endif
|
|
/*
|
|
* Program write.c - dump memory structures to file for iso9660 filesystem.
|
|
*
|
|
* Written by Eric Youngdale (1993).
|
|
*
|
|
* Copyright 1993 Yggdrasil Computing, Incorporated
|
|
* Copyright (c) 1999-2016 J. Schilling
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
/* APPLE_HYB James Pearson j.pearson@ge.ucl.ac.uk 23/2/2000 */
|
|
|
|
/* DUPLICATES_ONCE Alex Kopylov cdrtools@bootcd.ru 19.06.2004 */
|
|
|
|
#include "mkisofs.h"
|
|
#include <schily/time.h>
|
|
#include <schily/fcntl.h>
|
|
#ifdef SORTING
|
|
#include "match.h"
|
|
#endif /* SORTING */
|
|
#include <schily/errno.h>
|
|
#include <schily/schily.h>
|
|
#include <schily/checkerr.h>
|
|
#ifdef DVD_AUD_VID
|
|
#include "dvd_reader.h"
|
|
#include "dvd_file.h"
|
|
#include "ifo_read.h"
|
|
#endif
|
|
#ifdef APPLE_HYB
|
|
#include <schily/ctype.h>
|
|
#endif
|
|
|
|
#ifdef VMS
|
|
#include "vms.h"
|
|
#endif
|
|
|
|
#define SIZEOF_UDF_EXT_ATTRIBUTE_COMMON 50
|
|
|
|
/* Max number of sectors we will write at one time */
|
|
#define NSECT 32
|
|
|
|
#define INSERTMACRESFORK 1
|
|
|
|
/* Counters for statistics */
|
|
|
|
LOCAL int table_size = 0;
|
|
LOCAL int total_dir_size = 0;
|
|
LOCAL int rockridge_size = 0;
|
|
LOCAL struct directory **pathlist;
|
|
LOCAL int next_path_index = 1;
|
|
LOCAL int sort_goof;
|
|
|
|
LOCAL int is_rr_dir = 0;
|
|
|
|
struct output_fragment *out_tail;
|
|
struct output_fragment *out_list;
|
|
|
|
EXPORT struct iso_primary_descriptor vol_desc;
|
|
LOCAL int vol_desc_sum;
|
|
|
|
#ifndef APPLE_HFS_HYB
|
|
char *hfs_error = __("no error");
|
|
#endif
|
|
|
|
LOCAL int xawrite __PR((void *buffer, int size, int count,
|
|
FILE *file, int submode, BOOL islast));
|
|
EXPORT void xfwrite __PR((void *buffer, int size, int count,
|
|
FILE *file, int submode, BOOL islast));
|
|
LOCAL int assign_directory_addresses __PR((struct directory *node));
|
|
#if defined(APPLE_HYB) || defined(USE_LARGEFILES)
|
|
LOCAL void write_one_file __PR((char *filename, off_t size,
|
|
FILE *outfile, off_t off,
|
|
int isrfile, unsigned rba));
|
|
#else
|
|
LOCAL void write_one_file __PR((char *filename, off_t size,
|
|
FILE *outfile));
|
|
#endif
|
|
#ifdef UDF
|
|
LOCAL void write_udf_symlink __PR((char *filename, off_t size,
|
|
FILE *outfile));
|
|
#endif
|
|
LOCAL void write_files __PR((FILE *outfile));
|
|
#if 0
|
|
LOCAL void dump_filelist __PR((void));
|
|
#endif
|
|
LOCAL int compare_dirs __PR((const void *rr, const void *ll));
|
|
EXPORT int sort_directory __PR((struct directory_entry **sort_dir,
|
|
int rr));
|
|
LOCAL int root_gen __PR((void));
|
|
LOCAL BOOL assign_file_addresses __PR((struct directory *dpnt, BOOL isnest));
|
|
LOCAL void free_one_directory __PR((struct directory *dpnt));
|
|
LOCAL void free_directories __PR((struct directory *dpnt));
|
|
EXPORT void generate_one_directory __PR((struct directory *dpnt,
|
|
FILE *outfile));
|
|
LOCAL void build_pathlist __PR((struct directory *node));
|
|
LOCAL int compare_paths __PR((void const *r, void const *l));
|
|
LOCAL int generate_path_tables __PR((void));
|
|
EXPORT void memcpy_max __PR((char *to, char *from, int max));
|
|
EXPORT void outputlist_insert __PR((struct output_fragment *frag));
|
|
LOCAL int file_write __PR((FILE *outfile));
|
|
LOCAL int pvd_write __PR((FILE *outfile));
|
|
LOCAL int xpvd_write __PR((FILE *outfile));
|
|
LOCAL int evd_write __PR((FILE *outfile));
|
|
LOCAL int vers_write __PR((FILE *outfile));
|
|
LOCAL int graftcp __PR((char *to, char *from, char *ep));
|
|
LOCAL int pathcp __PR((char *to, char *from, char *ep));
|
|
LOCAL int pathtab_write __PR((FILE *outfile));
|
|
LOCAL int exten_write __PR((FILE *outfile));
|
|
EXPORT int oneblock_size __PR((UInt32_t starting_extent));
|
|
LOCAL int pathtab_size __PR((UInt32_t starting_extent));
|
|
LOCAL int startpad_size __PR((UInt32_t starting_extent));
|
|
LOCAL int interpad_size __PR((UInt32_t starting_extent));
|
|
LOCAL int endpad_size __PR((UInt32_t starting_extent));
|
|
LOCAL int file_gen __PR((void));
|
|
LOCAL int dirtree_dump __PR((void));
|
|
LOCAL int dirtree_fixup __PR((UInt32_t starting_extent));
|
|
LOCAL int dirtree_size __PR((UInt32_t starting_extent));
|
|
LOCAL int ext_size __PR((UInt32_t starting_extent));
|
|
LOCAL int dirtree_write __PR((FILE *outfile));
|
|
LOCAL int dirtree_cleanup __PR((FILE *outfile));
|
|
LOCAL int startpad_write __PR((FILE *outfile));
|
|
LOCAL int interpad_write __PR((FILE *outfile));
|
|
LOCAL int endpad_write __PR((FILE *outfile));
|
|
#ifdef APPLE_HYB
|
|
LOCAL int hfs_pad;
|
|
LOCAL int hfs_get_parms __PR((char *key));
|
|
LOCAL void hfs_file_gen __PR((UInt32_t start_extent));
|
|
LOCAL void gen_prepboot __PR((void));
|
|
EXPORT Ulong get_adj_size __PR((int Csize));
|
|
EXPORT int adj_size __PR((int Csize, UInt32_t start_extent, int extra));
|
|
EXPORT void adj_size_other __PR((struct directory *dpnt));
|
|
LOCAL int hfs_hce_write __PR((FILE *outfile));
|
|
EXPORT int insert_padding_file __PR((int size));
|
|
#endif /* APPLE_HYB */
|
|
|
|
#ifdef SORTING
|
|
LOCAL int compare_sort __PR((const void *rr, const void *ll));
|
|
LOCAL void reassign_link_addresses __PR((struct directory *dpnt));
|
|
LOCAL int sort_file_addresses __PR((void));
|
|
#endif /* SORTING */
|
|
|
|
/*
|
|
* Routines to actually write the disc. We write sequentially so that
|
|
* we could write a tape, or write the disc directly
|
|
*/
|
|
#define FILL_SPACE(X) memset(vol_desc.X, ' ', sizeof (vol_desc.X))
|
|
|
|
EXPORT void
|
|
xfwrite(buffer, size, count, file, submode, islast)
|
|
void *buffer;
|
|
int size;
|
|
int count;
|
|
FILE *file;
|
|
int submode;
|
|
BOOL islast;
|
|
{
|
|
/*
|
|
* This is a hack that could be made better.
|
|
* XXXIs this the only place?
|
|
* It is definitely needed on Operating Systems that do not allow to
|
|
* write files that are > 2GB. If the system is fast enough to be able
|
|
* to feed 1400 KB/s writing speed of a DVD-R drive, use stdout.
|
|
* If the system cannot do this reliable, you need to use this hacky
|
|
* option.
|
|
*/
|
|
static int idx = 0;
|
|
|
|
#ifdef XFWRITE_DEBUG
|
|
if (count != 1 || (size % 2048) != 0)
|
|
error(_("Count: %d, size: %d\n"), count, size);
|
|
#endif
|
|
if (count == 0 || size == 0) {
|
|
errmsgno(EX_BAD,
|
|
_("Implementation botch, write 0 bytes (size %d count %d).\n"),
|
|
size, count);
|
|
abort();
|
|
}
|
|
|
|
if (split_output != 0 &&
|
|
(idx == 0 || ftell(file) >= ((off_t)1024 * 1024 * 1024))) {
|
|
char nbuf[512];
|
|
extern char *outfile;
|
|
|
|
if (idx == 0)
|
|
unlink(outfile);
|
|
sprintf(nbuf, "%s_%02d", outfile, idx++);
|
|
file = freopen(nbuf, "wb", file);
|
|
if (file == NULL) {
|
|
comerr(_("Cannot open '%s'.\n"), nbuf);
|
|
}
|
|
}
|
|
while (count) {
|
|
int got;
|
|
|
|
seterrno(0);
|
|
if (osecsize != 0)
|
|
got = xawrite(buffer, size, count, file, submode, islast);
|
|
else
|
|
got = fwrite(buffer, size, count, file);
|
|
|
|
if (got <= 0) {
|
|
comerr(_("cannot fwrite %d*%d\n"), size, count);
|
|
}
|
|
/*
|
|
* This comment is in hope to prevent silly people from
|
|
* e.g. SuSE (who did not yet learn C but believe that
|
|
* they need to patch other peoples code) from changing the
|
|
* next cast into an illegal lhs cast expression.
|
|
* The cast below is the correct way to handle the problem.
|
|
* The (void *) cast is to avoid a GCC warning like:
|
|
* "warning: dereferencing type-punned pointer will break \
|
|
* strict-aliasing rules"
|
|
* which is wrong this code. (void *) introduces a compatible
|
|
* intermediate type in the cast list.
|
|
*/
|
|
count -= got, *(char **)(void *)&buffer += size * got;
|
|
}
|
|
}
|
|
|
|
LOCAL int
|
|
xawrite(buffer, size, count, file, submode, islast)
|
|
void *buffer;
|
|
int size;
|
|
int count;
|
|
FILE *file;
|
|
int submode;
|
|
BOOL islast;
|
|
{
|
|
register char *p = buffer;
|
|
register int amt = size * count;
|
|
register int n;
|
|
struct xa_subhdr subhdr[2];
|
|
|
|
if (osecsize == 2048)
|
|
return (fwrite(buffer, size, count, file));
|
|
|
|
if (amt % 2048)
|
|
comerrno(EX_BAD,
|
|
_("Trying to write %d bytes (not a multiple of 2048).\n"),
|
|
amt);
|
|
|
|
subhdr[0].file_number = subhdr[1].file_number = 0;
|
|
subhdr[0].channel_number = subhdr[1].channel_number = 0;
|
|
subhdr[0].coding = subhdr[1].coding = 0;
|
|
|
|
while (amt > 0) {
|
|
#ifdef LATER
|
|
if (submode < 0)
|
|
subhdr[0].sub_mode = subhdr[1].sub_mode = XA_SUBH_DATA;
|
|
else
|
|
subhdr[0].sub_mode = subhdr[1].sub_mode = submode;
|
|
#else
|
|
subhdr[0].sub_mode = subhdr[1].sub_mode = XA_SUBH_DATA;
|
|
#endif
|
|
|
|
if ((amt <= 2048) && islast) {
|
|
subhdr[0].sub_mode = subhdr[1].sub_mode
|
|
|= (XA_SUBH_EOR|XA_SUBH_EOF);
|
|
}
|
|
n = fwrite(subhdr, sizeof (subhdr), 1, file);
|
|
if (n <= 0)
|
|
return (n);
|
|
|
|
n = fwrite(p, 2048, 1, file);
|
|
if (n <= 0)
|
|
return (n);
|
|
|
|
p += 2048;
|
|
amt -= 2048;
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
#ifdef APPLE_HYB
|
|
/*
|
|
* use the deferred_write struct to store info about the hfs_boot_file
|
|
*/
|
|
LOCAL struct deferred_write mac_boot;
|
|
|
|
#endif /* APPLE_HYB */
|
|
LOCAL struct deferred_write *dw_head = NULL,
|
|
*dw_tail = NULL;
|
|
|
|
UInt32_t last_extent_written = 0;
|
|
LOCAL Uint path_table_index;
|
|
EXPORT time_t begun;
|
|
EXPORT struct timeval tv_begun;
|
|
|
|
/*
|
|
* We recursively walk through all of the directories and assign extent
|
|
* numbers to them. We have already assigned extent numbers to everything that
|
|
* goes in front of them
|
|
*/
|
|
LOCAL int
|
|
assign_directory_addresses(node)
|
|
struct directory *node;
|
|
{
|
|
int dir_size;
|
|
struct directory *dpnt;
|
|
|
|
dpnt = node;
|
|
|
|
while (dpnt) {
|
|
/* skip if it's hidden */
|
|
if (dpnt->dir_flags & INHIBIT_ISO9660_ENTRY) {
|
|
dpnt = dpnt->next;
|
|
continue;
|
|
}
|
|
/*
|
|
* If we already have an extent for this (i.e. it came from a
|
|
* multisession disc), then don't reassign a new extent.
|
|
*/
|
|
dpnt->path_index = next_path_index++;
|
|
if (dpnt->extent == 0) {
|
|
dpnt->extent = last_extent;
|
|
dir_size = ISO_BLOCKS(dpnt->size);
|
|
|
|
last_extent += dir_size;
|
|
|
|
/*
|
|
* Leave room for the CE entries for this directory.
|
|
* Keep them close to the reference directory so that
|
|
* access will be quick.
|
|
*/
|
|
if (dpnt->ce_bytes) {
|
|
last_extent += ISO_BLOCKS(dpnt->ce_bytes);
|
|
}
|
|
}
|
|
if (dpnt->subdir) {
|
|
assign_directory_addresses(dpnt->subdir);
|
|
}
|
|
dpnt = dpnt->next;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
#if defined(APPLE_HYB) || defined(USE_LARGEFILES)
|
|
LOCAL void
|
|
write_one_file(filename, size, outfile, off, isrfile, rba)
|
|
char *filename;
|
|
off_t size;
|
|
FILE *outfile;
|
|
off_t off;
|
|
int isrfile;
|
|
unsigned rba;
|
|
#else
|
|
LOCAL void
|
|
write_one_file(filename, size, outfile)
|
|
char *filename;
|
|
off_t size;
|
|
FILE *outfile;
|
|
#endif /* APPLE_HYB || USE_LARGEFILES */
|
|
{
|
|
/*
|
|
* It seems that there are still stone age C-compilers
|
|
* around.
|
|
* The Metrowerks C found on BeOS/PPC does not allow
|
|
* more than 32kB of local vars.
|
|
* As we do not need to call write_one_file() recursively
|
|
* we make buffer static.
|
|
*/
|
|
#ifdef __BEOS__
|
|
static char buffer[SECTOR_SIZE * NSECT];
|
|
#else
|
|
char buffer[SECTOR_SIZE * NSECT];
|
|
#endif
|
|
FILE *infile;
|
|
off_t remain;
|
|
int use;
|
|
int unroundeduse;
|
|
int bytestowrite = 0; /* Dummy init. to serve GCC bug */
|
|
int correctedsize = 0;
|
|
|
|
|
|
if ((infile = fopen(filename, "rb")) == NULL) {
|
|
if (!errhidden(E_OPEN, filename)) {
|
|
if (!errwarnonly(E_OPEN, filename))
|
|
;
|
|
errmsg(_("Cannot open '%s'.\n"), filename);
|
|
(void) errabort(E_OPEN, filename, TRUE);
|
|
}
|
|
}
|
|
#if defined(APPLE_HYB) || defined(USE_LARGEFILES)
|
|
if (infile)
|
|
fseek(infile, off, SEEK_SET);
|
|
#if defined(INSERTMACRESFORK) && defined(UDF)
|
|
if (isrfile && use_udf) {
|
|
memset(buffer, 0, sizeof (buffer));
|
|
udf_set_extattr_freespace((Uchar *)buffer, size, rba);
|
|
xfwrite(buffer, SECTOR_SIZE, 1, outfile, XA_SUBH_DATA, 1);
|
|
last_extent_written++;
|
|
|
|
memset(buffer, 0, sizeof (buffer));
|
|
udf_set_extattr_macresfork((Uchar *)buffer, size, rba);
|
|
xfwrite(buffer, SIZEOF_UDF_EXT_ATTRIBUTE_COMMON, 1, outfile, XA_SUBH_DATA, 1);
|
|
correctedsize = SIZEOF_UDF_EXT_ATTRIBUTE_COMMON;
|
|
}
|
|
#endif
|
|
#endif /* APPLE_HYB || USE_LARGEFILES */
|
|
remain = size;
|
|
|
|
while (remain > 0) {
|
|
int amt;
|
|
|
|
unroundeduse = use = (remain > SECTOR_SIZE * NSECT - 1 ?
|
|
NSECT * SECTOR_SIZE : remain);
|
|
use = ISO_ROUND_UP(use); /* Round up to nearest sector */
|
|
/* boundary */
|
|
memset(buffer, 0, use);
|
|
seterrno(0);
|
|
if (infile) {
|
|
#ifdef VMS
|
|
amt = fread(buffer, 1, use, infile);
|
|
#else
|
|
amt = ffileread(infile, buffer, use);
|
|
#endif
|
|
} else {
|
|
amt = use;
|
|
}
|
|
if (amt < use && amt != remain) {
|
|
/*
|
|
* Note that mkisofs is not star and no 100% archiver.
|
|
* We only detect file growth if the new size does not
|
|
* match 'use' at the last read.
|
|
*/
|
|
if (geterrno() == 0) {
|
|
if (!errhidden(amt > remain ? E_GROW:E_SHRINK, filename)) {
|
|
if (!errwarnonly(amt < remain ? E_SHRINK:E_GROW, filename)) {
|
|
errmsgno(EX_BAD,
|
|
_("Try to use the option -data-change-warn\n"));
|
|
errmsgno(EX_BAD,
|
|
_("Files should not change while mkisofs is running.\n"));
|
|
}
|
|
errmsgno(EX_BAD,
|
|
_("File '%s' did %s.\n"),
|
|
filename,
|
|
amt < remain ?
|
|
_("shrink"):_("grow"));
|
|
(void) errabort(amt < remain ?
|
|
E_SHRINK:E_GROW,
|
|
filename, TRUE);
|
|
}
|
|
} else if (!errhidden(E_READ, filename)) {
|
|
if (!errwarnonly(E_READ, filename))
|
|
;
|
|
errmsg(_("Cannot read from '%s'\n"), filename);
|
|
(void) errabort(E_READ, filename, TRUE);
|
|
}
|
|
amt = remain; /* Fake success */
|
|
if (infile) {
|
|
fclose(infile); /* Prevent furthe failure */
|
|
infile = NULL;
|
|
}
|
|
}
|
|
#if defined(APPLE_HYB) && defined(INSERTMACRESFORK) && defined(UDF)
|
|
if (unroundeduse == remain && isrfile && use_udf && correctedsize) {
|
|
/* adjust the last block to write according to correctedsize */
|
|
if (use - unroundeduse == correctedsize) {
|
|
bytestowrite = use;
|
|
correctedsize = 0;
|
|
} else if (use - unroundeduse > correctedsize) {
|
|
bytestowrite = use - correctedsize;
|
|
correctedsize = 0;
|
|
} else if (use - unroundeduse < correctedsize) {
|
|
bytestowrite = unroundeduse;
|
|
correctedsize -= use - unroundeduse;
|
|
}
|
|
} else {
|
|
bytestowrite = use;
|
|
}
|
|
#else
|
|
bytestowrite = use;
|
|
#endif
|
|
|
|
xfwrite(buffer, bytestowrite, 1, outfile,
|
|
XA_SUBH_DATA, remain <= (SECTOR_SIZE * NSECT));
|
|
last_extent_written += use / SECTOR_SIZE;
|
|
#if 0
|
|
if ((last_extent_written % 1000) < use / SECTOR_SIZE) {
|
|
fprintf(stderr, "%d..", last_extent_written);
|
|
}
|
|
#else
|
|
if (verbose > 0 &&
|
|
(int)(last_extent_written % (gui ? 500 : 5000)) <
|
|
use / SECTOR_SIZE) {
|
|
time_t now;
|
|
time_t the_end;
|
|
double frac;
|
|
|
|
time(&now);
|
|
frac = last_extent_written / (1.0 * last_extent);
|
|
the_end = begun + (now - begun) / frac;
|
|
#ifndef NO_FLOATINGPOINT
|
|
fprintf(stderr, _("%6.2f%% done, estimate finish %s"),
|
|
frac * 100., ctime(&the_end));
|
|
#else
|
|
fprintf(stderr, _("%3d.%-02d%% done, estimate finish %s"),
|
|
(int)(frac * 100.),
|
|
(int)((frac+.00005) * 10000.)%100,
|
|
ctime(&the_end));
|
|
#endif
|
|
fflush(stderr);
|
|
}
|
|
#endif
|
|
remain -= use;
|
|
}
|
|
#ifdef APPLE_HYB
|
|
#if defined(INSERTMACRESFORK) && defined(UDF)
|
|
if (isrfile && use_udf && correctedsize) {
|
|
if (ISO_ROUND_UP(size) < ISO_ROUND_UP(size + SIZEOF_UDF_EXT_ATTRIBUTE_COMMON)) {
|
|
memset(buffer, 0, sizeof (buffer));
|
|
xfwrite(buffer, SECTOR_SIZE - correctedsize, 1, outfile, XA_SUBH_DATA, 1);
|
|
last_extent_written++;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
if (infile)
|
|
fclose(infile);
|
|
} /* write_one_file(... */
|
|
|
|
#ifdef UDF
|
|
LOCAL void
|
|
write_udf_symlink(filename, size, outfile)
|
|
char *filename;
|
|
off_t size;
|
|
FILE *outfile;
|
|
{
|
|
static char buffer[SECTOR_SIZE * NSECT];
|
|
off_t remain = sizeof (buffer);
|
|
int use;
|
|
|
|
if (udf_get_symlinkcontents(filename, buffer, &remain) < 0) {
|
|
comerr(_("Cannot open smylink '%s'\n"), filename);
|
|
}
|
|
if (remain != size) {
|
|
comerrno(EX_BAD, _("Symlink '%s' did %s.\n"),
|
|
filename,
|
|
size > remain ?
|
|
_("shrink"):_("grow"));
|
|
}
|
|
use = (remain > SECTOR_SIZE * NSECT - 1 ?
|
|
NSECT * SECTOR_SIZE : remain);
|
|
use = ISO_ROUND_UP(use);
|
|
xfwrite(buffer, use, 1, outfile,
|
|
XA_SUBH_DATA, remain <= (SECTOR_SIZE * NSECT));
|
|
last_extent_written += use / SECTOR_SIZE;
|
|
|
|
} /* write_udf_symlink(... */
|
|
#endif
|
|
|
|
LOCAL void
|
|
write_files(outfile)
|
|
FILE *outfile;
|
|
{
|
|
struct deferred_write *dwpnt,
|
|
*dwnext;
|
|
unsigned rba = 0;
|
|
|
|
dwpnt = dw_head;
|
|
while (dwpnt) {
|
|
/*#define DEBUG*/
|
|
#ifdef DEBUG
|
|
fprintf(stderr,
|
|
_("The file name is %s and pad is %d, size is %lld and extent is %d\n"),
|
|
dwpnt->name, dwpnt->pad,
|
|
(Llong)dwpnt->size, dwpnt->extent);
|
|
#endif
|
|
if (dwpnt->table) {
|
|
xfwrite(dwpnt->table, ISO_ROUND_UP(dwpnt->size), 1,
|
|
outfile,
|
|
XA_SUBH_DATA, TRUE);
|
|
last_extent_written += ISO_BLOCKS(dwpnt->size);
|
|
table_size += dwpnt->size;
|
|
/* fprintf(stderr, _("Size %lld "), (Llong)dwpnt->size); */
|
|
free(dwpnt->table);
|
|
dwpnt->table = NULL;
|
|
} else {
|
|
|
|
#ifdef VMS
|
|
vms_write_one_file(dwpnt->name, dwpnt->size, outfile);
|
|
#else
|
|
#ifdef UDF
|
|
if ((dwpnt->dw_flags & IS_SYMLINK) && use_udf && create_udfsymlinks) {
|
|
write_udf_symlink(dwpnt->name, dwpnt->size, outfile);
|
|
} else {
|
|
#endif /* UDF */
|
|
#ifdef APPLE_HYB
|
|
#if defined(INSERTMACRESFORK) && defined(UDF)
|
|
if (file_is_resource(dwpnt->name, dwpnt->hfstype) && (dwpnt->size > 0) && use_udf) {
|
|
rba = dwpnt->extent;
|
|
} else {
|
|
rba = 0;
|
|
}
|
|
#endif /* INSERTMACRESFORK && UDF */
|
|
write_one_file(dwpnt->name, dwpnt->size, outfile, dwpnt->off,
|
|
file_is_resource(dwpnt->name, dwpnt->hfstype) && (dwpnt->size > 0), rba);
|
|
#else
|
|
#ifdef USE_LARGEFILES
|
|
write_one_file(dwpnt->name, dwpnt->size, outfile, dwpnt->off, 0, 0);
|
|
#else
|
|
write_one_file(dwpnt->name, dwpnt->size, outfile);
|
|
#endif
|
|
#endif /* APPLE_HYB */
|
|
#ifdef UDF
|
|
}
|
|
#endif
|
|
#endif /* VMS */
|
|
free(dwpnt->name);
|
|
dwpnt->name = NULL;
|
|
}
|
|
|
|
|
|
#ifndef DVD_AUD_VID
|
|
#define dvd_aud_vid_flag 0
|
|
#endif
|
|
|
|
#ifndef APPLE_HYB
|
|
#define apple_hyb 0
|
|
#endif
|
|
|
|
#if defined(APPLE_HYB) || defined(DVD_AUD_VID)
|
|
|
|
if ((apple_hyb && !donotwrite_macpart) || (dvd_aud_vid_flag & DVD_SPEC_VIDEO)) {
|
|
/*
|
|
* we may have to pad out ISO files to work with HFS
|
|
* clump sizes
|
|
*/
|
|
char blk[SECTOR_SIZE];
|
|
Uint i;
|
|
|
|
for (i = 0; i < dwpnt->pad; i++)
|
|
xfwrite(blk, SECTOR_SIZE, 1, outfile, 0, FALSE);
|
|
|
|
last_extent_written += dwpnt->pad;
|
|
}
|
|
#endif /* APPLE_HYB || DVD_AUD_VID */
|
|
|
|
|
|
dwnext = dwpnt;
|
|
dwpnt = dwpnt->next;
|
|
free(dwnext);
|
|
dwnext = NULL;
|
|
}
|
|
} /* write_files(... */
|
|
|
|
#if 0
|
|
LOCAL void
|
|
dump_filelist()
|
|
{
|
|
struct deferred_write *dwpnt;
|
|
|
|
dwpnt = dw_head;
|
|
while (dwpnt) {
|
|
fprintf(stderr, _("File %s\n"), dwpnt->name);
|
|
dwpnt = dwpnt->next;
|
|
}
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
#endif
|
|
|
|
LOCAL int
|
|
compare_dirs(rr, ll)
|
|
const void *rr;
|
|
const void *ll;
|
|
{
|
|
char *rpnt,
|
|
*lpnt;
|
|
struct directory_entry **r,
|
|
**l;
|
|
|
|
r = (struct directory_entry **)rr;
|
|
l = (struct directory_entry **)ll;
|
|
rpnt = (*r)->isorec.name;
|
|
lpnt = (*l)->isorec.name;
|
|
|
|
#ifdef APPLE_HYB
|
|
/*
|
|
* resource fork MUST (not sure if this is true for HFS volumes) be
|
|
* before the data fork - so force it here
|
|
*/
|
|
if ((*r)->assoc && (*r)->assoc == (*l))
|
|
return (1);
|
|
if ((*l)->assoc && (*l)->assoc == (*r))
|
|
return (-1);
|
|
#endif /* APPLE_HYB */
|
|
|
|
/*
|
|
* If the names are the same, multiple extent sections of the same file
|
|
* are sorted by part number. If the part numbers do not differ, this
|
|
* is an error.
|
|
*/
|
|
if (strcmp(rpnt, lpnt) == 0) {
|
|
#ifdef USE_LARGEFILES
|
|
if ((*r)->mxpart < (*l)->mxpart)
|
|
return (-1);
|
|
else if ((*r)->mxpart > (*l)->mxpart)
|
|
return (1);
|
|
#endif
|
|
errmsgno(EX_BAD,
|
|
_("Error: '%s' and '%s' have the same ISO9660 name '%s'.\n"),
|
|
(*r)->whole_name, (*l)->whole_name,
|
|
rpnt);
|
|
sort_goof++;
|
|
}
|
|
/* Check we don't have the same RR name */
|
|
if (use_RockRidge && !is_rr_dir) {
|
|
/*
|
|
* entries *can* have the same RR name in the "rr_moved"
|
|
* directory so skip checks if we're in reloc_dir
|
|
*/
|
|
if (strcmp((*r)->name, (*l)->name) == 0) {
|
|
errmsgno(EX_BAD,
|
|
_("Error: '%s' and '%s' have the same Rock Ridge name '%s'.\n"),
|
|
(*r)->whole_name, (*l)->whole_name,
|
|
(*r)->name);
|
|
sort_goof++;
|
|
}
|
|
}
|
|
/*
|
|
* Put the '.' and '..' entries on the head of the sorted list. For
|
|
* normal ASCII, this always happens to be the case, but out of band
|
|
* characters cause this not to be the case sometimes.
|
|
* FIXME(eric) - these tests seem redundant, in that the name is never
|
|
* assigned these values. It will instead be \000 or \001, and thus
|
|
* should always be sorted correctly. I need to figure out why I
|
|
* thought I needed this in the first place.
|
|
*/
|
|
#if 0
|
|
if (strcmp(rpnt, ".") == 0)
|
|
return (-1);
|
|
if (strcmp(lpnt, ".") == 0)
|
|
return (1);
|
|
|
|
if (strcmp(rpnt, "..") == 0)
|
|
return (-1);
|
|
if (strcmp(lpnt, "..") == 0)
|
|
return (1);
|
|
#else
|
|
/*
|
|
* The code above is wrong (as explained in Eric's comment), leading to
|
|
* incorrect sort order iff the -L option ("allow leading dots") is in
|
|
* effect and a directory contains entries that start with a dot.
|
|
* (TF, Tue Dec 29 13:49:24 CET 1998)
|
|
*/
|
|
if ((*r)->isorec.name_len[0] == 1 && *rpnt == 0)
|
|
return (-1); /* '.' */
|
|
if ((*l)->isorec.name_len[0] == 1 && *lpnt == 0)
|
|
return (1);
|
|
|
|
if ((*r)->isorec.name_len[0] == 1 && *rpnt == 1)
|
|
return (-1); /* '..' */
|
|
if ((*l)->isorec.name_len[0] == 1 && *lpnt == 1)
|
|
return (1);
|
|
#endif
|
|
|
|
while (*rpnt && *lpnt) {
|
|
if (*rpnt == ';' && *lpnt != ';')
|
|
return (-1);
|
|
if (*rpnt != ';' && *lpnt == ';')
|
|
return (1);
|
|
|
|
if (*rpnt == ';' && *lpnt == ';')
|
|
return (0);
|
|
|
|
if (*rpnt == '.' && *lpnt != '.')
|
|
return (-1);
|
|
if (*rpnt != '.' && *lpnt == '.')
|
|
return (1);
|
|
|
|
if ((unsigned char) *rpnt < (unsigned char) *lpnt)
|
|
return (-1);
|
|
if ((unsigned char) *rpnt > (unsigned char) *lpnt)
|
|
return (1);
|
|
rpnt++;
|
|
lpnt++;
|
|
}
|
|
if (*rpnt)
|
|
return (1);
|
|
if (*lpnt)
|
|
return (-1);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Function: sort_directory
|
|
*
|
|
* Purpose: Sort the directory in the appropriate ISO9660
|
|
* order.
|
|
*
|
|
* Notes: Returns 0 if OK, returns > 0 if an error occurred.
|
|
*/
|
|
EXPORT int
|
|
sort_directory(sort_dir, rr)
|
|
struct directory_entry **sort_dir;
|
|
int rr;
|
|
{
|
|
int dcount = 0;
|
|
int xcount = 0;
|
|
int j;
|
|
int i,
|
|
len;
|
|
struct directory_entry *s_entry;
|
|
struct directory_entry **sortlist;
|
|
|
|
/* need to keep a count of how many entries are hidden */
|
|
s_entry = *sort_dir;
|
|
while (s_entry) {
|
|
if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY)
|
|
xcount++;
|
|
dcount++;
|
|
s_entry = s_entry->next;
|
|
}
|
|
|
|
if (dcount == 0) {
|
|
return (0);
|
|
}
|
|
/* OK, now we know how many there are. Build a vector for sorting. */
|
|
sortlist = (struct directory_entry **)
|
|
e_malloc(sizeof (struct directory_entry *) * dcount);
|
|
|
|
j = dcount - xcount;
|
|
dcount = 0;
|
|
s_entry = *sort_dir;
|
|
while (s_entry) {
|
|
if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) {
|
|
/* put any hidden entries at the end of the vector */
|
|
sortlist[j++] = s_entry;
|
|
} else {
|
|
sortlist[dcount] = s_entry;
|
|
dcount++;
|
|
}
|
|
len = s_entry->isorec.name_len[0];
|
|
s_entry->isorec.name[len] = 0;
|
|
s_entry = s_entry->next;
|
|
}
|
|
|
|
/* Each directory is required to contain at least . and .. */
|
|
if (dcount < 2) {
|
|
errmsgno(EX_BAD,
|
|
_("Directory size too small (. or .. may be missing)\n"));
|
|
sort_goof = 1;
|
|
|
|
} else {
|
|
/* only sort the non-hidden entries */
|
|
sort_goof = 0;
|
|
is_rr_dir = rr;
|
|
#ifdef PROTOTYPES
|
|
qsort(sortlist, dcount, sizeof (struct directory_entry *),
|
|
(int (*) (const void *, const void *)) compare_dirs);
|
|
#else
|
|
qsort(sortlist, dcount, sizeof (struct directory_entry *),
|
|
compare_dirs);
|
|
#endif
|
|
|
|
/*
|
|
* Now reassemble the linked list in the proper sorted order
|
|
* We still need the hidden entries, as they may be used in
|
|
* the Joliet tree.
|
|
*/
|
|
for (i = 0; i < dcount + xcount - 1; i++) {
|
|
sortlist[i]->next = sortlist[i + 1];
|
|
}
|
|
|
|
sortlist[dcount + xcount - 1]->next = NULL;
|
|
*sort_dir = sortlist[0];
|
|
}
|
|
|
|
free(sortlist);
|
|
sortlist = NULL;
|
|
return (sort_goof);
|
|
}
|
|
|
|
LOCAL int
|
|
root_gen()
|
|
{
|
|
init_fstatbuf();
|
|
|
|
root_record.length[0] = 1 +
|
|
offsetof(struct iso_directory_record, name[0]);
|
|
root_record.ext_attr_length[0] = 0;
|
|
set_733((char *)root_record.extent, root->extent);
|
|
set_733((char *)root_record.size, ISO_ROUND_UP(root->size));
|
|
iso9660_date(root_record.date, root_statbuf.st_mtime);
|
|
root_record.flags[0] = ISO_DIRECTORY;
|
|
root_record.file_unit_size[0] = 0;
|
|
root_record.interleave[0] = 0;
|
|
set_723(root_record.volume_sequence_number, volume_sequence_number);
|
|
root_record.name_len[0] = 1;
|
|
return (0);
|
|
}
|
|
|
|
#ifdef SORTING
|
|
/*
|
|
* sorts deferred_write entries based on the sort weight
|
|
*/
|
|
LOCAL int
|
|
compare_sort(rr, ll)
|
|
const void *rr;
|
|
const void *ll;
|
|
{
|
|
struct deferred_write **r;
|
|
struct deferred_write **l;
|
|
int r_sort;
|
|
int l_sort;
|
|
|
|
r = (struct deferred_write **)rr;
|
|
l = (struct deferred_write **)ll;
|
|
r_sort = (*r)->s_entry->sort;
|
|
l_sort = (*l)->s_entry->sort;
|
|
|
|
if (r_sort != l_sort)
|
|
return (r_sort < l_sort ? 1 : -1);
|
|
else
|
|
return ((*r)->extent - (*l)->extent);
|
|
}
|
|
|
|
/*
|
|
* reassign start extents to files that are "hard links" to
|
|
* files that may have been sorted
|
|
*/
|
|
LOCAL void
|
|
reassign_link_addresses(dpnt)
|
|
struct directory *dpnt;
|
|
{
|
|
struct directory_entry *s_entry;
|
|
struct file_hash *s_hash;
|
|
|
|
while (dpnt) {
|
|
s_entry = dpnt->contents;
|
|
for (s_entry = dpnt->contents; s_entry; s_entry = s_entry->next) {
|
|
/* link files have already been given the weight NOT_SORTED */
|
|
if (s_entry->sort != NOT_SORTED)
|
|
continue;
|
|
|
|
/* update the start extent */
|
|
s_hash = find_hash(s_entry);
|
|
if (s_hash) {
|
|
set_733((char *)s_entry->isorec.extent, s_hash->starting_block);
|
|
s_entry->starting_block = s_hash->starting_block;
|
|
}
|
|
}
|
|
if (dpnt->subdir) {
|
|
reassign_link_addresses(dpnt->subdir);
|
|
}
|
|
|
|
dpnt = dpnt->next;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* sort files in order of the given sort weight
|
|
*/
|
|
LOCAL int
|
|
sort_file_addresses()
|
|
{
|
|
struct deferred_write *dwpnt;
|
|
struct deferred_write **sortlist;
|
|
struct directory_entry *s_entry;
|
|
UInt32_t start_extent;
|
|
int num = 0;
|
|
int i;
|
|
|
|
/* need to store start extents for linked files */
|
|
flush_hash();
|
|
|
|
/* find out how many files we have */
|
|
dwpnt = dw_head;
|
|
while (dwpnt) {
|
|
num++;
|
|
dwpnt = dwpnt->next;
|
|
}
|
|
|
|
/* return if we have none */
|
|
if (num == 0) {
|
|
return (1);
|
|
}
|
|
|
|
/* save the start extent of the first file */
|
|
start_extent = dw_head->extent;
|
|
|
|
/* set up vector to store entries */
|
|
sortlist = (struct deferred_write **)
|
|
e_malloc(sizeof (struct deferred_write *) * num);
|
|
|
|
for (i = 0, dwpnt = dw_head; i < num; i++, dwpnt = dwpnt->next)
|
|
sortlist[i] = dwpnt;
|
|
|
|
/* sort the list */
|
|
#ifdef PROTOTYPES
|
|
qsort(sortlist, num, sizeof (struct deferred_write *),
|
|
(int (*)(const void *, const void *))compare_sort);
|
|
#else
|
|
qsort(sortlist, num, sizeof (struct deferred_write *), compare_sort);
|
|
#endif
|
|
|
|
/* reconstruct the linked list */
|
|
for (i = 0; i < num-1; i++) {
|
|
sortlist[i]->next = sortlist[i+1];
|
|
}
|
|
|
|
sortlist[num-1]->next = NULL;
|
|
dw_head = sortlist[0];
|
|
|
|
free(sortlist);
|
|
|
|
/* set the new start extents for the sorted list */
|
|
for (i = 0, dwpnt = dw_head; i < num; i++, dwpnt = dwpnt->next) {
|
|
s_entry = dwpnt->s_entry;
|
|
dwpnt->extent = s_entry->starting_block = start_extent;
|
|
|
|
if (s_entry->de_flags & MULTI_EXTENT) {
|
|
struct directory_entry *s_e;
|
|
UInt32_t ext = start_extent;
|
|
|
|
/*
|
|
* For unknown reason, we sometimes get mxroot as
|
|
* part of the chain and sometime it's missing.
|
|
* Be careful to distinct between the mxroot entry and
|
|
* others to select both corectly in a conservative way.
|
|
*/
|
|
s_entry->mxroot->starting_block = start_extent;
|
|
set_733((char *)s_entry->mxroot->isorec.extent,
|
|
start_extent);
|
|
start_extent += ISO_BLOCKS(s_entry->mxroot->size);
|
|
|
|
for (s_e = s_entry;
|
|
s_e && s_e->mxroot == s_entry->mxroot;
|
|
s_e = s_e->next) {
|
|
if (s_e == s_entry->mxroot)
|
|
continue;
|
|
|
|
set_733((char *)s_e->isorec.extent, ext);
|
|
s_entry->starting_block = ext;
|
|
ext += ISO_BLOCKS(s_e->size);
|
|
}
|
|
} else {
|
|
set_733((char *)s_entry->isorec.extent, start_extent);
|
|
start_extent += ISO_BLOCKS(s_entry->size);
|
|
}
|
|
#ifdef DVD_AUD_VID
|
|
/*
|
|
* Shouldn't this be done for every type of sort? Otherwise
|
|
* we will loose every pad info we add if we sort the files
|
|
*/
|
|
if (dvd_aud_vid_flag & DVD_SPEC_VIDEO) {
|
|
start_extent += dwpnt->pad;
|
|
}
|
|
#endif /* DVD_AUD_VID */
|
|
|
|
/* cache start extents for any linked files */
|
|
add_hash(s_entry);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
#endif /* SORTING */
|
|
|
|
|
|
|
|
LOCAL BOOL
|
|
assign_file_addresses(dpnt, isnest)
|
|
struct directory *dpnt;
|
|
BOOL isnest;
|
|
{
|
|
struct directory *finddir;
|
|
struct directory_entry *s_entry;
|
|
struct file_hash *s_hash;
|
|
struct deferred_write *dwpnt;
|
|
char whole_path[PATH_MAX];
|
|
#ifdef DVD_AUD_VID
|
|
char dvd_path[PATH_MAX];
|
|
title_set_info_t *title_set_info = NULL;
|
|
char *p;
|
|
#endif
|
|
BOOL ret = FALSE;
|
|
|
|
while (dpnt) {
|
|
#ifdef DVD_AUD_VID
|
|
if ((dvd_aud_vid_flag & DVD_SPEC_VIDEO) && root == dpnt->parent &&
|
|
((p = strstr(dpnt->whole_name, "VIDEO_TS")) != 0)&&
|
|
strcmp(p, "VIDEO_TS") == 0) {
|
|
int maxlen = strlen(dpnt->whole_name)-8+1;
|
|
|
|
if (maxlen > sizeof (dvd_path))
|
|
maxlen = sizeof (dvd_path);
|
|
strlcpy(dvd_path, dpnt->whole_name, maxlen);
|
|
#ifdef DEBUG
|
|
fprintf(stderr, _("Found 'VIDEO_TS', the path is %s \n"), dvd_path);
|
|
#endif
|
|
title_set_info = DVDGetFileSet(dvd_path);
|
|
if (title_set_info == 0) {
|
|
/*
|
|
* Do not switch off -dvd-video but let is fail later.
|
|
*/
|
|
/* dvd_aud_vid_flag &= ~DVD_SPEC_VIDEO;*/
|
|
errmsgno(EX_BAD, _("Unable to parse DVD-Video structures.\n"));
|
|
} else {
|
|
ret = TRUE;
|
|
}
|
|
}
|
|
#endif /* DVD_AUD_VID */
|
|
|
|
for (s_entry = dpnt->contents; s_entry;
|
|
s_entry = s_entry->next) {
|
|
/*
|
|
* If we already have an extent for this entry, then
|
|
* don't assign a new one. It must have come from a
|
|
* previous session on the disc. Note that we don't
|
|
* end up scheduling the thing for writing either.
|
|
*/
|
|
if (get_733(s_entry->isorec.extent) != 0) {
|
|
continue;
|
|
}
|
|
/*
|
|
* This saves some space if there are symlinks present.
|
|
* If this is a multi-extent file, we get mxpart == 1
|
|
* from find_hash().
|
|
*/
|
|
s_hash = find_hash(s_entry);
|
|
if (s_hash) {
|
|
if (verbose > 2) {
|
|
fprintf(stderr, _("Cache hit for '%s%s%s'\n"),
|
|
s_entry->filedir->de_name,
|
|
SPATH_SEPARATOR,
|
|
s_entry->name);
|
|
}
|
|
s_entry->starting_block = s_hash->starting_block;
|
|
set_733((char *)s_entry->isorec.extent,
|
|
s_hash->starting_block);
|
|
set_733((char *)s_entry->isorec.size,
|
|
s_hash->size);
|
|
#ifdef USE_LARGEFILES
|
|
if (s_entry->de_flags & MULTI_EXTENT) {
|
|
struct directory_entry *s_e;
|
|
unsigned int ext = s_hash->starting_block;
|
|
|
|
/*
|
|
* Skip the multi extent root entry.
|
|
*/
|
|
if (s_entry->mxpart == 0)
|
|
continue;
|
|
/*
|
|
* The directory is sorted, so we should
|
|
* see s_entry->mxpart == 1 first.
|
|
*/
|
|
if (s_entry->mxpart != 1) {
|
|
comerrno(EX_BAD,
|
|
_("Panic: Multi extent parts for %s not sorted.\n"),
|
|
s_entry->whole_name);
|
|
}
|
|
s_entry->mxroot->starting_block = ext;
|
|
for (s_e = s_entry;
|
|
s_e && s_e->mxroot == s_entry->mxroot;
|
|
s_e = s_e->next) {
|
|
set_733((char *)s_e->isorec.extent,
|
|
ext);
|
|
ext += ISO_BLOCKS(s_e->size);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef SORTING
|
|
/* check for non-directory files */
|
|
if (do_sort && ((s_entry->isorec.flags[0] & ISO_DIRECTORY) == 0)) {
|
|
/* make sure the real file has the highest weighting */
|
|
s_hash->de->sort = MAX(s_entry->sort, s_hash->de->sort);
|
|
/* flag this as a potential non-sorted file */
|
|
s_entry->sort = NOT_SORTED;
|
|
}
|
|
#endif /* SORTING */
|
|
continue;
|
|
}
|
|
/*
|
|
* If this is for a directory that is not a . or
|
|
* a .. entry, then look up the information for the
|
|
* entry. We have already assigned extents for
|
|
* directories, so we just need to fill in the blanks
|
|
* here.
|
|
*/
|
|
if (strcmp(s_entry->name, ".") != 0 &&
|
|
strcmp(s_entry->name, "..") != 0 &&
|
|
s_entry->isorec.flags[0] & ISO_DIRECTORY) {
|
|
finddir = dpnt->subdir;
|
|
while (finddir && finddir->self != s_entry) {
|
|
finddir = finddir->next;
|
|
}
|
|
if (!finddir) {
|
|
#ifdef DVD_AUD_VID
|
|
if (title_set_info != 0) {
|
|
DVDFreeFileSet(title_set_info);
|
|
}
|
|
#endif
|
|
comerrno(EX_BAD,
|
|
_("Fatal goof - could not find dir entry for '%s'\n"),
|
|
s_entry->name);
|
|
}
|
|
set_733((char *)s_entry->isorec.extent,
|
|
finddir->extent);
|
|
s_entry->starting_block = finddir->extent;
|
|
s_entry->size = ISO_ROUND_UP(finddir->size);
|
|
total_dir_size += s_entry->size;
|
|
add_hash(s_entry);
|
|
set_733((char *)s_entry->isorec.size,
|
|
ISO_ROUND_UP(finddir->size));
|
|
continue;
|
|
}
|
|
/*
|
|
* If this is . or .., then look up the relevant info
|
|
* from the tables.
|
|
*/
|
|
if (strcmp(s_entry->name, ".") == 0) {
|
|
set_733((char *)s_entry->isorec.extent,
|
|
dpnt->extent);
|
|
|
|
/*
|
|
* Set these so that the hash table has the
|
|
* correct information
|
|
*/
|
|
s_entry->starting_block = dpnt->extent;
|
|
s_entry->size = ISO_ROUND_UP(dpnt->size);
|
|
|
|
add_hash(s_entry);
|
|
s_entry->starting_block = dpnt->extent;
|
|
set_733((char *)s_entry->isorec.size,
|
|
ISO_ROUND_UP(dpnt->size));
|
|
continue;
|
|
}
|
|
if (strcmp(s_entry->name, "..") == 0) {
|
|
if (dpnt == root) {
|
|
total_dir_size += root->size;
|
|
}
|
|
set_733((char *)s_entry->isorec.extent,
|
|
dpnt->parent->extent);
|
|
|
|
/*
|
|
* Set these so that the hash table has the
|
|
* correct information
|
|
*/
|
|
s_entry->starting_block = dpnt->parent->extent;
|
|
s_entry->size =
|
|
ISO_ROUND_UP(dpnt->parent->size);
|
|
|
|
add_hash(s_entry);
|
|
s_entry->starting_block = dpnt->parent->extent;
|
|
set_733((char *)s_entry->isorec.size,
|
|
ISO_ROUND_UP(dpnt->parent->size));
|
|
continue;
|
|
}
|
|
/*
|
|
* Some ordinary non-directory file. Just schedule
|
|
* the file to be written. This is all quite
|
|
* straightforward, just make a list and assign
|
|
* extents as we go. Once we get through writing all
|
|
* of the directories, we should be ready write out
|
|
* these files
|
|
*/
|
|
if (s_entry->size) {
|
|
dwpnt = (struct deferred_write *)
|
|
e_malloc(sizeof (struct deferred_write));
|
|
/* save this directory entry for later use */
|
|
dwpnt->s_entry = s_entry;
|
|
/* set the initial padding to zero */
|
|
dwpnt->pad = 0;
|
|
dwpnt->dw_flags = 0;
|
|
#ifdef DVD_AUD_VID
|
|
if ((dvd_aud_vid_flag & DVD_SPEC_VIDEO) && (title_set_info != 0)) {
|
|
int pad;
|
|
|
|
pad = DVDGetFilePad(title_set_info, s_entry->name);
|
|
if (pad < 0) {
|
|
errmsgno(EX_BAD,
|
|
_("Implementation botch. Video pad for file %s is %d\n"),
|
|
s_entry->name, pad),
|
|
comerrno(EX_BAD,
|
|
_("Either the *.IFO file is bad or you found a mkisofs bug.\n"));
|
|
}
|
|
dwpnt->pad = pad;
|
|
if (verbose > 0 && pad != 0) {
|
|
fprintf(stderr,
|
|
_("The pad was %d for file %s\n"),
|
|
dwpnt->pad, s_entry->name);
|
|
}
|
|
}
|
|
#endif /* DVD_AUD_VID */
|
|
#ifdef APPLE_HYB
|
|
/*
|
|
* maybe an offset to start of the real
|
|
* file/fork
|
|
*/
|
|
dwpnt->off = s_entry->hfs_off;
|
|
dwpnt->hfstype = s_entry->hfs_type;
|
|
#else
|
|
dwpnt->off = (off_t)0;
|
|
#endif /* APPLE_HYB */
|
|
if (s_entry->inode == TABLE_INODE) {
|
|
dwpnt->table = s_entry->table;
|
|
dwpnt->name = NULL;
|
|
sprintf(whole_path, "%s%s%s",
|
|
s_entry->filedir->whole_name,
|
|
SPATH_SEPARATOR, trans_tbl);
|
|
} else {
|
|
dwpnt->table = NULL;
|
|
strlcpy(whole_path,
|
|
s_entry->whole_name,
|
|
sizeof (whole_path));
|
|
dwpnt->name = e_strdup(whole_path);
|
|
}
|
|
dwpnt->next = NULL;
|
|
dwpnt->size = s_entry->size;
|
|
dwpnt->extent = last_extent;
|
|
set_733((char *)s_entry->isorec.extent,
|
|
last_extent);
|
|
s_entry->starting_block = last_extent;
|
|
#ifdef USE_LARGEFILES
|
|
/*
|
|
* Update the entries for multi-section files
|
|
* as we now know the starting extent numbers.
|
|
*/
|
|
if (s_entry->de_flags & MULTI_EXTENT) {
|
|
struct directory_entry *s_e;
|
|
unsigned int ext = last_extent;
|
|
|
|
/*
|
|
* Skip the multi extent root entry.
|
|
*/
|
|
if (s_entry->mxpart == 0) {
|
|
if (dwpnt->name)
|
|
free(dwpnt->name);
|
|
free(dwpnt);
|
|
continue;
|
|
}
|
|
/*
|
|
* The directory is sorted, so we should
|
|
* see s_entry->mxpart == 1 first.
|
|
*/
|
|
if (s_entry->mxpart != 1) {
|
|
comerrno(EX_BAD,
|
|
_("Panic: Multi extent parts for %s not sorted.\n"),
|
|
s_entry->whole_name);
|
|
}
|
|
dwpnt->size = s_entry->mxroot->size;
|
|
s_entry->mxroot->starting_block = ext;
|
|
/*
|
|
* Set the mxroot (mxpart == 0) to allow
|
|
* the UDF code to fetch the starting
|
|
* extent number.
|
|
*/
|
|
set_733((char *)s_entry->mxroot->isorec.extent, ext);
|
|
for (s_e = s_entry;
|
|
s_e && s_e->mxroot == s_entry->mxroot;
|
|
s_e = s_e->next) {
|
|
if (s_e->mxpart == 0)
|
|
continue;
|
|
set_733((char *)s_e->isorec.extent,
|
|
ext);
|
|
ext += ISO_BLOCKS(s_e->size);
|
|
}
|
|
add_hash(s_entry);
|
|
}
|
|
#endif
|
|
if (dw_tail) {
|
|
dw_tail->next = dwpnt;
|
|
dw_tail = dwpnt;
|
|
} else {
|
|
dw_head = dwpnt;
|
|
dw_tail = dwpnt;
|
|
}
|
|
add_hash(s_entry);
|
|
/*
|
|
* The cache holds the full size of the file
|
|
*/
|
|
last_extent += ISO_BLOCKS(dwpnt->size);
|
|
dwpnt->dw_flags = s_entry->de_flags;
|
|
#ifdef APPLE_HYB
|
|
#if defined(INSERTMACRESFORK) && defined(UDF)
|
|
if (file_is_resource(dwpnt->name, dwpnt->s_entry->hfs_type) && (dwpnt->size > 0) && use_udf) {
|
|
last_extent++;
|
|
if (ISO_ROUND_UP(dwpnt->size) <
|
|
ISO_ROUND_UP(dwpnt->size + SIZEOF_UDF_EXT_ATTRIBUTE_COMMON)) {
|
|
last_extent++;
|
|
}
|
|
}
|
|
#endif
|
|
#endif /* APPLE_HYB */
|
|
#ifdef DVD_AUD_VID
|
|
/* Shouldn't we always add the pad info? */
|
|
if (dvd_aud_vid_flag & DVD_SPEC_VIDEO) {
|
|
last_extent += dwpnt->pad;
|
|
}
|
|
#endif /* DVD_AUD_VID */
|
|
if (verbose > 2) {
|
|
fprintf(stderr, "%u %d %s\n",
|
|
s_entry->starting_block,
|
|
last_extent - 1, whole_path);
|
|
}
|
|
#ifdef DBG_ISO
|
|
if (ISO_BLOCKS(s_entry->size) > 500) {
|
|
fprintf(stderr,
|
|
_("Warning: large file '%s'\n"),
|
|
whole_path);
|
|
fprintf(stderr,
|
|
_("Starting block is %d\n"),
|
|
s_entry->starting_block);
|
|
fprintf(stderr,
|
|
_("Reported file size is %lld\n"),
|
|
(Llong)s_entry->size);
|
|
|
|
}
|
|
#endif
|
|
#ifdef NOT_NEEDED /* Never use this code if you like to create a DVD */
|
|
|
|
if (last_extent > (800000000 >> 11)) {
|
|
/* More than 800Mb? Punt */
|
|
fprintf(stderr,
|
|
_("Extent overflow processing file '%s'\n"),
|
|
whole_path);
|
|
fprintf(stderr,
|
|
_("Starting block is %d\n"),
|
|
s_entry->starting_block);
|
|
fprintf(stderr,
|
|
_("Reported file size is %lld\n"),
|
|
(Llong)s_entry->size);
|
|
exit(1);
|
|
}
|
|
#endif
|
|
continue;
|
|
}
|
|
/*
|
|
* This is for zero-length files. If we leave the
|
|
* extent 0, then we get screwed, because many readers
|
|
* simply drop files that have an extent of zero.
|
|
* Thus we leave the size 0, and just assign the
|
|
* extent number.
|
|
*/
|
|
set_733((char *)s_entry->isorec.extent, last_extent);
|
|
}
|
|
if (dpnt->subdir) {
|
|
if (assign_file_addresses(dpnt->subdir, TRUE))
|
|
ret = TRUE;
|
|
}
|
|
dpnt = dpnt->next;
|
|
}
|
|
#ifdef DVD_AUD_VID
|
|
if (title_set_info != NULL) {
|
|
DVDFreeFileSet(title_set_info);
|
|
}
|
|
if ((dvd_aud_vid_flag & DVD_SPEC_VIDEO)&& !ret && !isnest) {
|
|
errmsgno(EX_BAD,
|
|
_("Could not find correct 'VIDEO_TS' directory.\n"));
|
|
}
|
|
#endif /* DVD_AUD_VID */
|
|
return (ret);
|
|
} /* assign_file_addresses(... */
|
|
|
|
LOCAL void
|
|
free_one_directory(dpnt)
|
|
struct directory *dpnt;
|
|
{
|
|
struct directory_entry *s_entry;
|
|
struct directory_entry *s_entry_d;
|
|
|
|
s_entry = dpnt->contents;
|
|
while (s_entry) {
|
|
s_entry_d = s_entry;
|
|
s_entry = s_entry->next;
|
|
|
|
if (s_entry_d->rr_attributes) {
|
|
free(s_entry_d->rr_attributes);
|
|
s_entry_d->rr_attributes = NULL;
|
|
}
|
|
if (s_entry_d->name != NULL) {
|
|
free(s_entry_d->name);
|
|
s_entry_d->name = NULL;
|
|
}
|
|
if (s_entry_d->whole_name != NULL) {
|
|
free(s_entry_d->whole_name);
|
|
s_entry_d->whole_name = NULL;
|
|
}
|
|
#ifdef APPLE_HYB
|
|
if (apple_both && s_entry_d->hfs_ent && !s_entry_d->assoc &&
|
|
(s_entry_d->isorec.flags[0] & ISO_MULTIEXTENT) == 0) {
|
|
free(s_entry_d->hfs_ent);
|
|
}
|
|
#endif /* APPLE_HYB */
|
|
|
|
#ifdef DUPLICATES_ONCE
|
|
if (s_entry_d->digest_fast) {
|
|
|
|
if (s_entry_d->digest_full &&
|
|
(s_entry_d->digest_full != s_entry_d->digest_fast))
|
|
free(s_entry_d->digest_full);
|
|
|
|
free(s_entry_d->digest_fast);
|
|
|
|
s_entry_d->digest_fast = NULL;
|
|
s_entry_d->digest_full = NULL;
|
|
}
|
|
#endif
|
|
free(s_entry_d);
|
|
s_entry_d = NULL;
|
|
}
|
|
dpnt->contents = NULL;
|
|
} /* free_one_directory(... */
|
|
|
|
LOCAL void
|
|
free_directories(dpnt)
|
|
struct directory *dpnt;
|
|
{
|
|
while (dpnt) {
|
|
free_one_directory(dpnt);
|
|
if (dpnt->subdir)
|
|
free_directories(dpnt->subdir);
|
|
dpnt = dpnt->next;
|
|
}
|
|
}
|
|
|
|
EXPORT void
|
|
generate_one_directory(dpnt, outfile)
|
|
struct directory *dpnt;
|
|
FILE *outfile;
|
|
{
|
|
unsigned int ce_address = 0;
|
|
char *ce_buffer;
|
|
unsigned int ce_index = 0;
|
|
unsigned int ce_size;
|
|
unsigned int dir_index;
|
|
char *directory_buffer;
|
|
int new_reclen;
|
|
struct directory_entry *s_entry;
|
|
struct directory_entry *s_entry_d;
|
|
unsigned int total_size;
|
|
|
|
total_size = ISO_ROUND_UP(dpnt->size);
|
|
directory_buffer = (char *)e_malloc(total_size);
|
|
memset(directory_buffer, 0, total_size);
|
|
dir_index = 0;
|
|
|
|
ce_size = ISO_ROUND_UP(dpnt->ce_bytes);
|
|
ce_buffer = NULL;
|
|
|
|
if (ce_size > 0) {
|
|
ce_buffer = (char *)e_malloc(ce_size);
|
|
memset(ce_buffer, 0, ce_size);
|
|
|
|
ce_index = 0;
|
|
|
|
/* Absolute sector address of CE entries for this directory */
|
|
ce_address = last_extent_written + (total_size >> 11);
|
|
}
|
|
s_entry = dpnt->contents;
|
|
while (s_entry) {
|
|
/* skip if it's hidden */
|
|
if (s_entry->de_flags & INHIBIT_ISO9660_ENTRY) {
|
|
s_entry = s_entry->next;
|
|
continue;
|
|
}
|
|
/*
|
|
* We do not allow directory entries to cross sector
|
|
* boundaries. Simply pad, and then start the next entry at
|
|
* the next sector
|
|
*/
|
|
new_reclen = s_entry->isorec.length[0];
|
|
if ((dir_index & (SECTOR_SIZE - 1)) + new_reclen >=
|
|
SECTOR_SIZE) {
|
|
dir_index = ISO_ROUND_UP(dir_index);
|
|
}
|
|
memcpy(directory_buffer + dir_index, &s_entry->isorec,
|
|
offsetof(struct iso_directory_record, name[0]) +
|
|
s_entry->isorec.name_len[0]);
|
|
dir_index += offsetof(struct iso_directory_record, name[0]) +
|
|
s_entry->isorec.name_len[0];
|
|
|
|
/* Add the Rock Ridge attributes, if present */
|
|
if (s_entry->rr_attr_size) {
|
|
if (dir_index & 1) {
|
|
directory_buffer[dir_index++] = 0;
|
|
}
|
|
/*
|
|
* If the RR attributes were too long, then write the
|
|
* CE records, as required.
|
|
*/
|
|
if (s_entry->rr_attr_size != s_entry->total_rr_attr_size) {
|
|
struct iso_xa_dir_record *xadp;
|
|
unsigned char *pnt;
|
|
int len,
|
|
nbytes;
|
|
|
|
/*
|
|
* Go through the entire record, first skip
|
|
* the XA record and then fix up the
|
|
* CE entries so that the extent and offset
|
|
* are correct
|
|
*/
|
|
pnt = s_entry->rr_attributes;
|
|
len = s_entry->total_rr_attr_size;
|
|
|
|
if (len >= 14) {
|
|
xadp = (struct iso_xa_dir_record *)pnt;
|
|
|
|
if (xadp->signature[0] == 'X' && xadp->signature[1] == 'A' &&
|
|
xadp->reserved[0] == '\0') {
|
|
len -= 14;
|
|
pnt += 14;
|
|
}
|
|
}
|
|
|
|
while (len > 3) {
|
|
#ifdef DEBUG
|
|
if (ce_size <= 0) {
|
|
fprintf(stderr,
|
|
_("Warning: ce_index(%d) && ce_address(%d) not initialized\n"),
|
|
ce_index, ce_address);
|
|
}
|
|
#endif
|
|
|
|
if (pnt[0] == 'C' && pnt[1] == 'E') {
|
|
nbytes = get_733((char *)pnt + 20);
|
|
|
|
if ((ce_index & (SECTOR_SIZE - 1)) + nbytes >=
|
|
SECTOR_SIZE) {
|
|
ce_index = ISO_ROUND_UP(ce_index);
|
|
}
|
|
set_733((char *)pnt + 4,
|
|
ce_address + (ce_index >> 11));
|
|
set_733((char *)pnt + 12,
|
|
ce_index & (SECTOR_SIZE - 1));
|
|
|
|
|
|
|
|
/*
|
|
* Now store the block in the
|
|
* ce buffer
|
|
*/
|
|
memcpy(ce_buffer + ce_index,
|
|
pnt + pnt[2], nbytes);
|
|
ce_index += nbytes;
|
|
if (ce_index & 1) {
|
|
ce_index++;
|
|
}
|
|
}
|
|
len -= pnt[2];
|
|
pnt += pnt[2];
|
|
}
|
|
|
|
}
|
|
rockridge_size += s_entry->total_rr_attr_size;
|
|
memcpy(directory_buffer + dir_index,
|
|
s_entry->rr_attributes,
|
|
s_entry->rr_attr_size);
|
|
dir_index += s_entry->rr_attr_size;
|
|
}
|
|
if (dir_index & 1) {
|
|
directory_buffer[dir_index++] = 0;
|
|
}
|
|
s_entry_d = s_entry;
|
|
s_entry = s_entry->next;
|
|
|
|
/*
|
|
* Joliet doesn't use the Rock Ridge attributes, so we free
|
|
* it here.
|
|
*/
|
|
if (s_entry_d->rr_attributes) {
|
|
free(s_entry_d->rr_attributes);
|
|
s_entry_d->rr_attributes = NULL;
|
|
}
|
|
}
|
|
|
|
if (dpnt->size != dir_index) {
|
|
errmsgno(EX_BAD,
|
|
_("Unexpected directory length %lld expected: %d '%s'\n"),
|
|
(Llong)dpnt->size,
|
|
dir_index, dpnt->de_name);
|
|
}
|
|
xfwrite(directory_buffer, total_size, 1, outfile, 0, FALSE);
|
|
last_extent_written += total_size >> 11;
|
|
free(directory_buffer);
|
|
directory_buffer = NULL;
|
|
|
|
if (ce_size > 0) {
|
|
if (ce_index != dpnt->ce_bytes) {
|
|
errmsgno(EX_BAD,
|
|
_("Continuation entry record length mismatch %d expected: %d.\n"),
|
|
ce_index, dpnt->ce_bytes);
|
|
}
|
|
xfwrite(ce_buffer, ce_size, 1, outfile, 0, FALSE);
|
|
last_extent_written += ce_size >> 11;
|
|
free(ce_buffer);
|
|
ce_buffer = NULL;
|
|
}
|
|
} /* generate_one_directory(... */
|
|
|
|
LOCAL void
|
|
build_pathlist(node)
|
|
struct directory *node;
|
|
{
|
|
struct directory *dpnt;
|
|
|
|
dpnt = node;
|
|
|
|
while (dpnt) {
|
|
/* skip if it's hidden */
|
|
if ((dpnt->dir_flags & INHIBIT_ISO9660_ENTRY) == 0)
|
|
pathlist[dpnt->path_index] = dpnt;
|
|
|
|
if (dpnt->subdir)
|
|
build_pathlist(dpnt->subdir);
|
|
dpnt = dpnt->next;
|
|
}
|
|
} /* build_pathlist(... */
|
|
|
|
LOCAL int
|
|
compare_paths(r, l)
|
|
void const *r;
|
|
void const *l;
|
|
{
|
|
struct directory const *ll = *(struct directory * const *) l;
|
|
struct directory const *rr = *(struct directory * const *) r;
|
|
|
|
if (rr->parent->path_index < ll->parent->path_index) {
|
|
return (-1);
|
|
}
|
|
if (rr->parent->path_index > ll->parent->path_index) {
|
|
return (1);
|
|
}
|
|
return (strcmp(rr->self->isorec.name, ll->self->isorec.name));
|
|
|
|
} /* compare_paths(... */
|
|
|
|
LOCAL int
|
|
generate_path_tables()
|
|
{
|
|
struct directory_entry *de = NULL;
|
|
struct directory *dpnt;
|
|
int fix;
|
|
int i;
|
|
int j;
|
|
int namelen;
|
|
char *npnt;
|
|
char *npnt1;
|
|
int tablesize;
|
|
|
|
/* First allocate memory for the tables and initialize the memory */
|
|
tablesize = path_blocks << 11;
|
|
path_table_m = (char *)e_malloc(tablesize);
|
|
path_table_l = (char *)e_malloc(tablesize);
|
|
memset(path_table_l, 0, tablesize);
|
|
memset(path_table_m, 0, tablesize);
|
|
|
|
/*
|
|
* Now start filling in the path tables. Start with root directory
|
|
*/
|
|
|
|
path_table_index = 0;
|
|
pathlist = (struct directory **)e_malloc(sizeof (struct directory *)
|
|
*next_path_index);
|
|
memset(pathlist, 0, sizeof (struct directory *) * next_path_index);
|
|
build_pathlist(root);
|
|
|
|
do {
|
|
fix = 0;
|
|
#ifdef PROTOTYPES
|
|
qsort(&pathlist[1], next_path_index - 1,
|
|
sizeof (struct directory *),
|
|
(int (*) (const void *, const void *)) compare_paths);
|
|
#else
|
|
qsort(&pathlist[1], next_path_index - 1,
|
|
sizeof (struct directory *),
|
|
compare_paths);
|
|
#endif
|
|
|
|
for (j = 1; j < next_path_index; j++) {
|
|
if (pathlist[j]->path_index != j) {
|
|
pathlist[j]->path_index = j;
|
|
fix++;
|
|
}
|
|
}
|
|
} while (fix);
|
|
|
|
for (j = 1; j < next_path_index; j++) {
|
|
dpnt = pathlist[j];
|
|
if (!dpnt) {
|
|
comerrno(EX_BAD, _("Entry %d not in path tables\n"), j);
|
|
}
|
|
npnt = dpnt->de_name;
|
|
|
|
/* So the root comes out OK */
|
|
if ((*npnt == 0) || (dpnt == root)) {
|
|
npnt = ".";
|
|
}
|
|
npnt1 = strrchr(npnt, PATH_SEPARATOR);
|
|
if (npnt1) {
|
|
npnt = npnt1 + 1;
|
|
}
|
|
de = dpnt->self;
|
|
if (!de) {
|
|
comerrno(EX_BAD,
|
|
_("Fatal ISO9660 goof - directory has amnesia\n"));
|
|
}
|
|
namelen = de->isorec.name_len[0];
|
|
|
|
path_table_l[path_table_index] = namelen;
|
|
path_table_m[path_table_index] = namelen;
|
|
path_table_index += 2;
|
|
|
|
set_731(path_table_l + path_table_index, dpnt->extent);
|
|
set_732(path_table_m + path_table_index, dpnt->extent);
|
|
path_table_index += 4;
|
|
|
|
set_721(path_table_l + path_table_index,
|
|
dpnt->parent->path_index);
|
|
set_722(path_table_m + path_table_index,
|
|
dpnt->parent->path_index);
|
|
|
|
if (dpnt->parent->path_index > 0xffff) {
|
|
static int warned = 0;
|
|
|
|
if (!warned) {
|
|
warned++;
|
|
errmsgno(EX_BAD,
|
|
_("Unable to generate sane path tables - too many directories (%u)\n"),
|
|
dpnt->parent->path_index);
|
|
if (!nolimitpathtables)
|
|
errmsgno(EX_BAD,
|
|
_("Try to use the option -no-limit-pathtables\n"));
|
|
}
|
|
if (!nolimitpathtables)
|
|
exit(EX_BAD);
|
|
/*
|
|
* Let it point to the root directory instead.
|
|
*/
|
|
set_721(path_table_l + path_table_index, 1);
|
|
set_722(path_table_m + path_table_index, 1);
|
|
}
|
|
|
|
path_table_index += 2;
|
|
|
|
for (i = 0; i < namelen; i++) {
|
|
path_table_l[path_table_index] = de->isorec.name[i];
|
|
path_table_m[path_table_index] = de->isorec.name[i];
|
|
path_table_index++;
|
|
}
|
|
if (path_table_index & 1) {
|
|
path_table_index++; /* For odd lengths we pad */
|
|
}
|
|
}
|
|
|
|
free(pathlist);
|
|
pathlist = NULL;
|
|
if (path_table_index != path_table_size) {
|
|
errmsgno(EX_BAD,
|
|
_("Path table lengths do not match %d expected: %d\n"),
|
|
path_table_index,
|
|
path_table_size);
|
|
}
|
|
return (0);
|
|
} /* generate_path_tables(... */
|
|
|
|
EXPORT void
|
|
memcpy_max(to, from, max)
|
|
char *to;
|
|
char *from;
|
|
int max;
|
|
{
|
|
int n = strlen(from);
|
|
|
|
if (n > max) {
|
|
n = max;
|
|
}
|
|
memcpy(to, from, n);
|
|
|
|
} /* memcpy_max(... */
|
|
|
|
EXPORT void
|
|
outputlist_insert(frag)
|
|
struct output_fragment *frag;
|
|
{
|
|
struct output_fragment *nfrag;
|
|
|
|
nfrag = e_malloc(sizeof (*frag));
|
|
movebytes(frag, nfrag, sizeof (*frag));
|
|
nfrag->of_start_extent = 0;
|
|
|
|
if (out_tail == NULL) {
|
|
out_list = out_tail = nfrag;
|
|
} else {
|
|
out_tail->of_next = nfrag;
|
|
out_tail = nfrag;
|
|
}
|
|
}
|
|
|
|
LOCAL int
|
|
file_write(outfile)
|
|
FILE *outfile;
|
|
{
|
|
Uint should_write;
|
|
|
|
#ifdef APPLE_HYB
|
|
char buffer[SECTOR_SIZE];
|
|
|
|
memset(buffer, 0, sizeof (buffer));
|
|
|
|
if (apple_hyb && !donotwrite_macpart) {
|
|
|
|
int i;
|
|
|
|
/*
|
|
* write out padding to round up to HFS allocation block
|
|
*/
|
|
for (i = 0; i < hfs_pad; i++)
|
|
xfwrite(buffer, sizeof (buffer), 1, outfile, 0, FALSE);
|
|
|
|
last_extent_written += hfs_pad;
|
|
}
|
|
#endif /* APPLE_HYB */
|
|
|
|
/*
|
|
* OK, all done with that crap. Now write out the directories. This is
|
|
* where the fur starts to fly, because we need to keep track of each
|
|
* file as we find it and keep track of where we put it.
|
|
*/
|
|
should_write = last_extent - session_start;
|
|
|
|
if (verbose > 2) {
|
|
#ifdef DBG_ISO
|
|
fprintf(stderr,
|
|
_("Total directory extents being written = %u\n"),
|
|
last_extent);
|
|
#endif
|
|
|
|
#ifdef APPLE_HYB
|
|
if (apple_hyb && !donotwrite_macpart)
|
|
fprintf(stderr,
|
|
_("Total extents scheduled to be written (inc HFS) = %u\n"),
|
|
last_extent - session_start);
|
|
else
|
|
#endif /* APPLE_HYB */
|
|
|
|
fprintf(stderr,
|
|
_("Total extents scheduled to be written = %u\n"),
|
|
last_extent - session_start);
|
|
}
|
|
/* Now write all of the files that we need. */
|
|
write_files(outfile);
|
|
|
|
#ifdef APPLE_HYB
|
|
/* write out extents/catalog/dt file */
|
|
if (apple_hyb && !donotwrite_macpart) {
|
|
|
|
xfwrite(hce->hfs_ce, HFS_BLOCKSZ, hce->hfs_tot_size, outfile, 0, FALSE);
|
|
|
|
/* round up to a whole CD block */
|
|
if (HFS_ROUND_UP(hce->hfs_tot_size) -
|
|
hce->hfs_tot_size * HFS_BLOCKSZ) {
|
|
xfwrite(buffer,
|
|
HFS_ROUND_UP(hce->hfs_tot_size) -
|
|
hce->hfs_tot_size * HFS_BLOCKSZ, 1, outfile, 0, FALSE);
|
|
}
|
|
last_extent_written += ISO_ROUND_UP(hce->hfs_tot_size *
|
|
HFS_BLOCKSZ) / SECTOR_SIZE;
|
|
|
|
/* write out HFS boot block */
|
|
if (mac_boot.name)
|
|
write_one_file(mac_boot.name, mac_boot.size, outfile,
|
|
mac_boot.off, 0, 0);
|
|
}
|
|
#endif /* APPLE_HYB */
|
|
|
|
/* The rest is just fluff. */
|
|
if (verbose == 0) {
|
|
return (0);
|
|
}
|
|
#ifdef APPLE_HYB
|
|
if (apple_hyb && !donotwrite_macpart) {
|
|
fprintf(stderr,
|
|
_("Total extents actually written (inc HFS) = %u\n"),
|
|
last_extent_written - session_start);
|
|
fprintf(stderr, _("(Size of ISO volume = %d, HFS extra = %d)\n"),
|
|
last_extent_written - session_start - hfs_extra,
|
|
hfs_extra);
|
|
} else
|
|
#else
|
|
fprintf(stderr, _("Total extents actually written = %d\n"),
|
|
last_extent_written - session_start);
|
|
#endif /* APPLE_HYB */
|
|
|
|
/* Hard links throw us off here */
|
|
if (should_write != (last_extent - session_start)) {
|
|
fprintf(stderr,
|
|
_("Number of extents written not what was predicted. Please fix.\n"));
|
|
fprintf(stderr, _("Predicted = %d, written = %d\n"),
|
|
should_write, last_extent);
|
|
}
|
|
fprintf(stderr, _("Total translation table size: %d\n"), table_size);
|
|
fprintf(stderr, _("Total rockridge attributes bytes: %d\n"),
|
|
rockridge_size);
|
|
fprintf(stderr, _("Total directory bytes: %d\n"), total_dir_size);
|
|
fprintf(stderr, _("Path table size(bytes): %d\n"), path_table_size);
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr,
|
|
"next extent, last_extent, last_extent_written %d %d %d\n",
|
|
next_extent, last_extent, last_extent_written);
|
|
#endif
|
|
|
|
return (0);
|
|
|
|
} /* iso_write(... */
|
|
|
|
/*
|
|
* Function to write the PVD for the disc.
|
|
*/
|
|
LOCAL int
|
|
pvd_write(outfile)
|
|
FILE *outfile;
|
|
{
|
|
char iso_time[17];
|
|
int should_write;
|
|
int i;
|
|
int s;
|
|
Uchar *cp;
|
|
extern ldate modification_date;
|
|
|
|
|
|
iso9660_ldate(iso_time, tv_begun.tv_sec, tv_begun.tv_usec * 1000, -100);
|
|
|
|
/* Next we write out the primary descriptor for the disc */
|
|
memset(&vol_desc, 0, sizeof (vol_desc));
|
|
vol_desc.type[0] = ISO_VD_PRIMARY;
|
|
strncpy(vol_desc.id, ISO_STANDARD_ID, sizeof (vol_desc.id));
|
|
vol_desc.version[0] = 1;
|
|
|
|
memset(vol_desc.system_id, ' ', sizeof (vol_desc.system_id));
|
|
memcpy_max(vol_desc.system_id, system_id, strlen(system_id));
|
|
|
|
memset(vol_desc.volume_id, ' ', sizeof (vol_desc.volume_id));
|
|
memcpy_max(vol_desc.volume_id, volume_id, strlen(volume_id));
|
|
|
|
should_write = last_extent - session_start;
|
|
set_733((char *)vol_desc.volume_space_size, should_write);
|
|
set_723(vol_desc.volume_set_size, volume_set_size);
|
|
set_723(vol_desc.volume_sequence_number, volume_sequence_number);
|
|
set_723(vol_desc.logical_block_size, SECTOR_SIZE);
|
|
|
|
/*
|
|
* The path tables are used by DOS based machines to cache directory
|
|
* locations
|
|
*/
|
|
set_733((char *)vol_desc.path_table_size, path_table_size);
|
|
set_731(vol_desc.type_l_path_table, path_table[0]);
|
|
set_731(vol_desc.opt_type_l_path_table, path_table[1]);
|
|
set_732(vol_desc.type_m_path_table, path_table[2]);
|
|
set_732(vol_desc.opt_type_m_path_table, path_table[3]);
|
|
|
|
/* Now we copy the actual root directory record */
|
|
memcpy(vol_desc.root_directory_record, &root_record,
|
|
offsetof(struct iso_directory_record, name[0]) + 1);
|
|
|
|
/*
|
|
* The rest is just fluff. It looks nice to fill in many of these
|
|
* fields, though.
|
|
*/
|
|
FILL_SPACE(volume_set_id);
|
|
if (volset_id)
|
|
memcpy_max(vol_desc.volume_set_id, volset_id, strlen(volset_id));
|
|
|
|
FILL_SPACE(publisher_id);
|
|
if (publisher)
|
|
memcpy_max(vol_desc.publisher_id, publisher, strlen(publisher));
|
|
|
|
FILL_SPACE(preparer_id);
|
|
if (preparer)
|
|
memcpy_max(vol_desc.preparer_id, preparer, strlen(preparer));
|
|
|
|
FILL_SPACE(application_id);
|
|
if (appid)
|
|
memcpy_max(vol_desc.application_id, appid, strlen(appid));
|
|
|
|
FILL_SPACE(copyright_file_id);
|
|
if (copyright)
|
|
memcpy_max(vol_desc.copyright_file_id, copyright,
|
|
strlen(copyright));
|
|
|
|
FILL_SPACE(abstract_file_id);
|
|
if (abstract)
|
|
memcpy_max(vol_desc.abstract_file_id, abstract,
|
|
strlen(abstract));
|
|
|
|
FILL_SPACE(bibliographic_file_id);
|
|
if (biblio)
|
|
memcpy_max(vol_desc.bibliographic_file_id, biblio,
|
|
strlen(biblio));
|
|
|
|
FILL_SPACE(creation_date);
|
|
FILL_SPACE(modification_date);
|
|
FILL_SPACE(expiration_date);
|
|
FILL_SPACE(effective_date);
|
|
vol_desc.file_structure_version[0] = 1;
|
|
FILL_SPACE(application_data);
|
|
|
|
iso9660_ldate(vol_desc.modification_date,
|
|
modification_date.l_sec,
|
|
modification_date.l_usec * 1000,
|
|
modification_date.l_gmtoff);
|
|
|
|
memcpy(vol_desc.creation_date, iso_time, 17);
|
|
memcpy(vol_desc.expiration_date, "0000000000000000", 17);
|
|
memcpy(vol_desc.effective_date, iso_time, 17);
|
|
|
|
if (use_XA) {
|
|
char *xap = &((char *)&vol_desc)[1024];
|
|
|
|
memcpy(&xap[0], "CD-XA001", 8); /* XA Sign. */
|
|
memcpy(&xap[8], "\0\0", 2); /* XA flags */
|
|
memcpy(&xap[10], "\0\0\0\0\0\0\0\0", 8); /* Start dir */
|
|
memcpy(&xap[18], "\0\0\0\0\0\0\0\0", 8); /* Reserved */
|
|
}
|
|
|
|
/*
|
|
* Compute a checksum to be used as a fingerprint in case we
|
|
* include correct inode/link-count information in the current image.
|
|
*/
|
|
for (i = 0, s = 0, cp = (Uchar *)&vol_desc; i < SECTOR_SIZE; i++) {
|
|
s += cp[i] & 0xFF;
|
|
}
|
|
vol_desc_sum = s;
|
|
|
|
/* if not a bootable cd do it the old way */
|
|
xfwrite(&vol_desc, SECTOR_SIZE, 1, outfile, 0, FALSE);
|
|
last_extent_written++;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Function to write the Extended PVD for the disc.
|
|
*/
|
|
LOCAL int
|
|
xpvd_write(outfile)
|
|
FILE *outfile;
|
|
{
|
|
vol_desc.type[0] = ISO_VD_SUPPLEMENTARY;
|
|
vol_desc.version[0] = 2;
|
|
vol_desc.file_structure_version[0] = 2;
|
|
|
|
/* if not a bootable cd do it the old way */
|
|
xfwrite(&vol_desc, SECTOR_SIZE, 1, outfile, 0, FALSE);
|
|
last_extent_written++;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Function to write the EVD for the disc.
|
|
*/
|
|
LOCAL int
|
|
evd_write(outfile)
|
|
FILE *outfile;
|
|
{
|
|
struct iso_primary_descriptor evol_desc;
|
|
|
|
/*
|
|
* Now write the end volume descriptor. Much simpler than the other
|
|
* one
|
|
*/
|
|
memset(&evol_desc, 0, sizeof (evol_desc));
|
|
evol_desc.type[0] = (unsigned char) ISO_VD_END;
|
|
strncpy(evol_desc.id, ISO_STANDARD_ID, sizeof (evol_desc.id));
|
|
evol_desc.version[0] = 1;
|
|
xfwrite(&evol_desc, SECTOR_SIZE, 1, outfile, 0, TRUE);
|
|
last_extent_written += 1;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Function to write the version information for the disc.
|
|
* Warning: Do not disable or change this function. The data created by this
|
|
* function is used to tell the filesystem driver in the OS kernel that this
|
|
* mkisofs version includes correct inode information.
|
|
*/
|
|
LOCAL int
|
|
vers_write(outfile)
|
|
FILE *outfile;
|
|
{
|
|
char vers[SECTOR_SIZE+1];
|
|
int X_ac;
|
|
char **X_av;
|
|
char *cp;
|
|
int i;
|
|
int idx = 4;
|
|
int len;
|
|
extern char version_string[];
|
|
extern int path_ind;
|
|
|
|
/* Now write the version descriptor. */
|
|
memset(vers, 0, sizeof (vers));
|
|
strcpy(vers, "MKI "); /* strcpy() OK here */
|
|
|
|
cp = vers;
|
|
X_ac = saved_ac();
|
|
X_av = saved_av();
|
|
strlcpy(&cp[idx], ctime(&begun), 26);
|
|
idx += 25;
|
|
strlcpy(&cp[idx], version_string, SECTOR_SIZE - idx);
|
|
idx += strlen(version_string);
|
|
for (i = 1; i < X_ac; i++) {
|
|
len = strlen(X_av[i]);
|
|
if ((idx + len + 2) >= SECTOR_SIZE)
|
|
break;
|
|
cp[idx++] = ' ';
|
|
/*
|
|
* Do not give away secret information when not in debug mode.
|
|
*/
|
|
if (debug)
|
|
strlcpy(&cp[idx], X_av[i], SECTOR_SIZE - idx);
|
|
else if (i >= path_ind)
|
|
len = graftcp(&cp[idx], X_av[i], &vers[SECTOR_SIZE-1]);
|
|
else if (X_av[i][0] == '/')
|
|
len = pathcp(&cp[idx], X_av[i], &vers[SECTOR_SIZE-1]);
|
|
else
|
|
strlcpy(&cp[idx], X_av[i], SECTOR_SIZE - idx);
|
|
idx += len;
|
|
}
|
|
|
|
cp[SECTOR_SIZE - 1] = '\0';
|
|
len = 0;
|
|
if (correct_inodes) {
|
|
/*
|
|
* Only add this fingerprint in case we include correct
|
|
* inode/link-count information in the current image.
|
|
*/
|
|
len = vol_desc_sum;
|
|
}
|
|
cp = &vers[SECTOR_SIZE - 1];
|
|
*(Uchar *)cp = len % 256;
|
|
len /= 256;
|
|
*(Uchar *)--cp = len % 256;
|
|
len /= 256;
|
|
*(Uchar *)--cp = len % 256;
|
|
|
|
xfwrite(vers, SECTOR_SIZE, 1, outfile, 0, TRUE);
|
|
last_extent_written += 1;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Avoid to write unwanted information into the version info string.
|
|
*/
|
|
LOCAL int
|
|
graftcp(to, from, ep)
|
|
char *to;
|
|
char *from;
|
|
char *ep;
|
|
{
|
|
int len = strlen(from);
|
|
char *node = NULL;
|
|
|
|
if (use_graft_ptrs)
|
|
node = findgequal(from);
|
|
|
|
if (node == NULL) {
|
|
len = 0;
|
|
node = from;
|
|
} else {
|
|
len = node - from;
|
|
*node = '\0';
|
|
strncpy(to, from, ep - to);
|
|
*node++ = '=';
|
|
to += len++;
|
|
*to++ = '=';
|
|
}
|
|
return (len + pathcp(to, node, ep));
|
|
}
|
|
|
|
LOCAL int
|
|
pathcp(to, from, ep)
|
|
char *to;
|
|
char *from;
|
|
char *ep;
|
|
{
|
|
int len = strlen(from);
|
|
char *p;
|
|
|
|
p = strrchr(from, '/');
|
|
if (p == NULL) {
|
|
strncpy(to, from, ep - to);
|
|
} else {
|
|
if (p[1] == '\0') {
|
|
--p;
|
|
while (p > from && *p != '/')
|
|
--p;
|
|
}
|
|
len = 0;
|
|
if (*p == '/') {
|
|
strncpy(to, "...", ep - to);
|
|
to += 3;
|
|
len = 3;
|
|
}
|
|
if (to < ep) {
|
|
strncpy(to, p, ep - to);
|
|
len += strlen(to);
|
|
}
|
|
}
|
|
return (len);
|
|
}
|
|
|
|
|
|
/*
|
|
* Function to write the path table for the disc.
|
|
*/
|
|
LOCAL int
|
|
pathtab_write(outfile)
|
|
FILE *outfile;
|
|
{
|
|
/* Next we write the path tables */
|
|
xfwrite(path_table_l, path_blocks << 11, 1, outfile, 0, FALSE);
|
|
xfwrite(path_table_m, path_blocks << 11, 1, outfile, 0, FALSE);
|
|
last_extent_written += 2 * path_blocks;
|
|
free(path_table_l);
|
|
free(path_table_m);
|
|
path_table_l = NULL;
|
|
path_table_m = NULL;
|
|
return (0);
|
|
}
|
|
|
|
LOCAL int
|
|
exten_write(outfile)
|
|
FILE *outfile;
|
|
{
|
|
xfwrite(extension_record, SECTOR_SIZE, 1, outfile, 0, FALSE);
|
|
last_extent_written++;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Functions to describe padding block at the start of the disc.
|
|
*/
|
|
EXPORT int
|
|
oneblock_size(starting_extent)
|
|
UInt32_t starting_extent;
|
|
{
|
|
last_extent++;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Functions to describe path table size.
|
|
*/
|
|
LOCAL int
|
|
pathtab_size(starting_extent)
|
|
UInt32_t starting_extent;
|
|
{
|
|
path_table[0] = starting_extent;
|
|
|
|
path_table[1] = 0;
|
|
path_table[2] = path_table[0] + path_blocks;
|
|
path_table[3] = 0;
|
|
last_extent += 2 * path_blocks;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Functions to describe padding blocks before PVD.
|
|
*/
|
|
LOCAL int
|
|
startpad_size(starting_extent)
|
|
UInt32_t starting_extent;
|
|
{
|
|
last_extent = session_start + 16;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Functions to describe padding blocks between sections.
|
|
*/
|
|
LOCAL int
|
|
interpad_size(starting_extent)
|
|
UInt32_t starting_extent;
|
|
{
|
|
int emod = 0;
|
|
|
|
#ifdef needed
|
|
starting_extent += 16; /* First add 16 pad blocks */
|
|
#endif
|
|
if ((emod = starting_extent % 16) != 0) {
|
|
starting_extent += 16 - emod; /* Now pad to mod 16 # */
|
|
}
|
|
last_extent = starting_extent;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Functions to describe padding blocks at end of disk.
|
|
*/
|
|
LOCAL int
|
|
endpad_size(starting_extent)
|
|
UInt32_t starting_extent;
|
|
{
|
|
starting_extent += 150; /* 150 pad blocks (post gap) */
|
|
last_extent = starting_extent;
|
|
return (0);
|
|
}
|
|
|
|
LOCAL int
|
|
file_gen()
|
|
{
|
|
#ifdef APPLE_HYB
|
|
UInt32_t start_extent = last_extent; /* orig ISO files start */
|
|
|
|
#endif /* APPLE_HYB */
|
|
|
|
if (!assign_file_addresses(root, FALSE)) {
|
|
#ifdef DVD_AUD_VID
|
|
if (dvd_aud_vid_flag & DVD_SPEC_VIDEO) {
|
|
comerrno(EX_BAD, _("Unable to make a DVD-Video image.\n"));
|
|
}
|
|
#else
|
|
;
|
|
/* EMPTY */
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef SORTING
|
|
if (do_sort) {
|
|
if (sort_file_addresses() == 0)
|
|
reassign_link_addresses(root);
|
|
}
|
|
#endif /* SORTING */
|
|
|
|
#ifdef APPLE_HYB
|
|
/*
|
|
* put this here for the time being - may when I've worked out how to
|
|
* use Eric's new system for creating/writing parts of the image it
|
|
* may move to it's own routine
|
|
*/
|
|
if (apple_hyb && !donotwrite_macpart)
|
|
hfs_file_gen(start_extent);
|
|
#ifdef PREP_BOOT
|
|
else if (use_prep_boot || use_chrp_boot)
|
|
gen_prepboot();
|
|
#endif /* PREP_BOOT */
|
|
#endif /* APPLE_HYB */
|
|
|
|
/*
|
|
* Do inode/hard link related stuff for non-directory type files.
|
|
*/
|
|
do_inode(root);
|
|
return (0);
|
|
}
|
|
|
|
LOCAL int
|
|
dirtree_dump()
|
|
{
|
|
if (verbose > 2) {
|
|
dump_tree(root);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
LOCAL int
|
|
dirtree_fixup(starting_extent)
|
|
UInt32_t starting_extent;
|
|
{
|
|
if (use_RockRidge && reloc_dir)
|
|
finish_cl_pl_entries();
|
|
|
|
/*
|
|
* Set the link count for directories to 2 + number of sub-directories.
|
|
*/
|
|
if (use_RockRidge)
|
|
do_dir_nlink(root);
|
|
return (0);
|
|
}
|
|
|
|
LOCAL int
|
|
dirtree_size(starting_extent)
|
|
UInt32_t starting_extent;
|
|
{
|
|
assign_directory_addresses(root);
|
|
return (0);
|
|
}
|
|
|
|
LOCAL int
|
|
ext_size(starting_extent)
|
|
UInt32_t starting_extent;
|
|
{
|
|
extern int extension_record_size;
|
|
struct directory_entry *s_entry;
|
|
|
|
extension_record_extent = starting_extent;
|
|
s_entry = root->contents;
|
|
set_733((char *)s_entry->rr_attributes + s_entry->rr_attr_size - 24,
|
|
extension_record_extent);
|
|
set_733((char *)s_entry->rr_attributes + s_entry->rr_attr_size - 8,
|
|
extension_record_size);
|
|
last_extent++;
|
|
return (0);
|
|
}
|
|
|
|
LOCAL int
|
|
dirtree_write(outfile)
|
|
FILE *outfile;
|
|
{
|
|
generate_iso9660_directories(root, outfile);
|
|
return (0);
|
|
}
|
|
|
|
LOCAL int
|
|
dirtree_cleanup(outfile)
|
|
FILE *outfile;
|
|
{
|
|
free_directories(root);
|
|
return (0);
|
|
}
|
|
|
|
LOCAL int
|
|
startpad_write(outfile)
|
|
FILE *outfile;
|
|
{
|
|
char buffer[SECTOR_SIZE];
|
|
int i;
|
|
int npad;
|
|
|
|
memset(buffer, 0, sizeof (buffer));
|
|
|
|
npad = session_start + 16 - last_extent_written;
|
|
|
|
for (i = 0; i < npad; i++) {
|
|
xfwrite(buffer, sizeof (buffer), 1, outfile, 0, FALSE);
|
|
}
|
|
|
|
last_extent_written += npad;
|
|
return (0);
|
|
}
|
|
|
|
LOCAL int
|
|
interpad_write(outfile)
|
|
FILE *outfile;
|
|
{
|
|
char buffer[SECTOR_SIZE];
|
|
int i;
|
|
int npad = 0;
|
|
|
|
memset(buffer, 0, sizeof (buffer));
|
|
|
|
#ifdef needed
|
|
npad = 16;
|
|
#endif
|
|
if ((i = last_extent_written % 16) != 0)
|
|
npad += 16 - i;
|
|
|
|
for (i = 0; i < npad; i++) {
|
|
xfwrite(buffer, sizeof (buffer), 1, outfile, 0, FALSE);
|
|
}
|
|
|
|
last_extent_written += npad;
|
|
return (0);
|
|
}
|
|
|
|
LOCAL int
|
|
endpad_write(outfile)
|
|
FILE *outfile;
|
|
{
|
|
char buffer[SECTOR_SIZE];
|
|
int i;
|
|
|
|
memset(buffer, 0, sizeof (buffer));
|
|
|
|
for (i = 0; i < 150; i++) {
|
|
xfwrite(buffer, sizeof (buffer), 1, outfile, 0, FALSE);
|
|
}
|
|
|
|
last_extent_written += 150;
|
|
return (0);
|
|
}
|
|
|
|
#ifdef APPLE_HYB
|
|
|
|
/*
|
|
* hfs_get_parms: get HFS parameters from the command line
|
|
*/
|
|
|
|
LOCAL int
|
|
hfs_get_parms(key)
|
|
char *key;
|
|
{
|
|
int ret = 0;
|
|
char *p;
|
|
|
|
if (hfs_parms == NULL)
|
|
return (ret);
|
|
|
|
if ((p = strstr(hfs_parms, key)) != NULL) {
|
|
p += strlen(key) + 1;
|
|
sscanf(p, "%d", &ret);
|
|
}
|
|
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* hfs_file_gen: set up "fake" HFS volume using the ISO9660 tree
|
|
*/
|
|
LOCAL void
|
|
hfs_file_gen(start_extent)
|
|
UInt32_t start_extent;
|
|
{
|
|
int Csize; /* clump size for HFS vol */
|
|
int loop;
|
|
UInt32_t last_extent_save = last_extent;
|
|
char *p;
|
|
|
|
/* allocate memory for the libhfs/mkisofs extra info */
|
|
hce = (hce_mem *) e_malloc(sizeof (hce_mem));
|
|
|
|
hce->error = (char *)e_malloc(1024);
|
|
|
|
/* mark as unallocated for use later */
|
|
hce->hfs_ce = hce->hfs_hdr = hce->hfs_map = 0;
|
|
|
|
/* reserve space for the label partition - if it is needed */
|
|
#ifdef PREP_BOOT
|
|
/* a PReP bootable partition needs the map.. */
|
|
if (gen_pt || use_prep_boot || use_chrp_boot)
|
|
#else
|
|
if (gen_pt)
|
|
#endif /* PREP_BOOT */
|
|
hce->hfs_map_size = HFS_MAP_SIZE;
|
|
else
|
|
hce->hfs_map_size = 0;
|
|
|
|
/* set the HFS parameter string to upper case */
|
|
if (hfs_parms) {
|
|
for (p = hfs_parms; *p; p++)
|
|
*p = toupper((*p & 0xFF));
|
|
}
|
|
|
|
/* set the initial factor to increase Catalog file size */
|
|
if ((hce->ctc_size = hfs_get_parms("CTC")) == 0)
|
|
hce->ctc_size = CTC;
|
|
|
|
/* set the max size of the Catalog file */
|
|
if ((hce->max_XTCsize = hfs_get_parms("MAX_XTCSIZE")) == 0)
|
|
hce->max_XTCsize = MAX_XTCSIZE;
|
|
|
|
/* set the number of time to try to make an HFS volume */
|
|
if ((loop = hfs_get_parms("CTC_LOOP")) == 0)
|
|
loop = CTC_LOOP;
|
|
|
|
/*
|
|
* "create" the HFS volume (just the header, catalog/extents files) if
|
|
* there's a problem with the Catalog file being too small, we keep on
|
|
* increasing the size (up to CTC_LOOP) times and try again.
|
|
* Unfortunately I don't know enough about the inner workings of HFS,
|
|
* so I can't workout the size of the Catalog file in advance (and I
|
|
* don't want to "grow" as is is normally allowed to), therefore, this
|
|
* approach is a bit over the top as it involves throwing away the
|
|
* "volume" we have created and trying again ...
|
|
*/
|
|
do {
|
|
hce->error[0] = '\0';
|
|
|
|
/* attempt to create the Mac volume */
|
|
#ifdef APPLE_HFS_HYB
|
|
Csize = make_mac_volume(root, start_extent);
|
|
#else
|
|
Csize = -1;
|
|
#endif
|
|
|
|
/* if we have a problem ... */
|
|
if (Csize < 0) {
|
|
/*
|
|
* we've made too many attempts, or got some other
|
|
* error
|
|
*/
|
|
if (loop == 0 || errno != HCE_ERROR) {
|
|
/* HCE_ERROR is not a valid errno value */
|
|
if (errno == HCE_ERROR)
|
|
errno = 0;
|
|
|
|
/* exit with the error */
|
|
if (*hce->error)
|
|
fprintf(stderr, "%s\n", hce->error);
|
|
perr(hfs_error);
|
|
} else {
|
|
/* increase Catalog file size factor */
|
|
hce->ctc_size *= CTC;
|
|
|
|
/*
|
|
* reset the initial "last_extent" and try
|
|
* again
|
|
*/
|
|
last_extent = last_extent_save;
|
|
}
|
|
} else {
|
|
/* everything OK - just carry on ... */
|
|
loop = 0;
|
|
}
|
|
}
|
|
while (loop--);
|
|
|
|
hfs_extra = HFS_ROUND_UP(hce->hfs_tot_size) / SECTOR_SIZE;
|
|
|
|
last_extent += hfs_extra;
|
|
|
|
/* generate the Mac label and HFS partition maps */
|
|
mac_boot.name = hfs_boot_file;
|
|
|
|
/*
|
|
* only generate the partition tables etc. if we are making a bootable
|
|
* CD - or if the -part option is given
|
|
*/
|
|
if (gen_pt) {
|
|
if (gen_mac_label(&mac_boot)) {
|
|
if (*hce->error)
|
|
fprintf(stderr, "%s\n", hce->error);
|
|
perr(hfs_error);
|
|
}
|
|
}
|
|
/* set Autostart filename if required */
|
|
if (autoname) {
|
|
if (autostart())
|
|
perr("Autostart filename must less than 12 characters");
|
|
}
|
|
/* finished with any HFS type errors */
|
|
free(hce->error);
|
|
hce->error = 0;
|
|
|
|
/*
|
|
* the ISO files need to start on a multiple of the HFS allocation
|
|
* blocks, so find out how much padding we need
|
|
*/
|
|
|
|
/*
|
|
* take in accout alignment of files wrt HFS volume start - remove any
|
|
* previous session as well
|
|
*/
|
|
start_extent -= session_start;
|
|
hfs_pad = ROUND_UP(start_extent*SECTOR_SIZE +
|
|
(hce->hfs_hdr_size + hce->hfs_map_size) * HFS_BLOCKSZ,
|
|
Csize) / SECTOR_SIZE;
|
|
|
|
hfs_pad -= (start_extent + (hce->hfs_hdr_size + hce->hfs_map_size) /
|
|
HFS_BLK_CONV);
|
|
|
|
#ifdef PREP_BOOT
|
|
gen_prepboot_label(hce->hfs_map);
|
|
#endif /* PREP_BOOT */
|
|
|
|
}
|
|
|
|
#ifdef PREP_BOOT
|
|
LOCAL void
|
|
gen_prepboot()
|
|
{
|
|
/*
|
|
* we need to allocate the hce struct since hce->hfs_map is used to
|
|
* generate the fdisk partition map required for PReP booting
|
|
*/
|
|
hce = (hce_mem *) e_malloc(sizeof (hce_mem));
|
|
|
|
/* mark as unallocated for use later */
|
|
hce->hfs_ce = hce->hfs_hdr = hce->hfs_map = 0;
|
|
|
|
/* reserve space for the label partition - if it is needed */
|
|
hce->hfs_map_size = HFS_MAP_SIZE;
|
|
|
|
hce->hfs_map = (unsigned char *) e_malloc(hce->hfs_map_size * HFS_BLOCKSZ);
|
|
gen_prepboot_label(hce->hfs_map);
|
|
}
|
|
|
|
#endif /* PREP_BOOT */
|
|
|
|
/*
|
|
* get_adj_size: get the ajusted size of the volume with the HFS
|
|
* allocation block size for each file
|
|
*/
|
|
EXPORT Ulong
|
|
get_adj_size(Csize)
|
|
int Csize;
|
|
{
|
|
struct deferred_write *dw;
|
|
Ulong size = 0;
|
|
int count = 0;
|
|
|
|
/* loop through all the files finding the new total size */
|
|
for (dw = dw_head; dw; dw = dw->next) {
|
|
size += (ROUND_UP(dw->size, Csize)/HFS_BLOCKSZ);
|
|
count++;
|
|
}
|
|
|
|
/*
|
|
* crude attempt to prevent overflows - HFS can only cope with a
|
|
* maximum of about 65536 forks (actually less) - this will trap cases
|
|
* when we have far too many files
|
|
*/
|
|
|
|
if (count >= 65536)
|
|
return (-1);
|
|
else
|
|
return (size);
|
|
}
|
|
|
|
/*
|
|
* adj_size: adjust the ISO record entries for all files
|
|
* based on the HFS allocation block size
|
|
*/
|
|
EXPORT int
|
|
adj_size(Csize, start_extent, extra)
|
|
int Csize;
|
|
UInt32_t start_extent;
|
|
int extra;
|
|
{
|
|
struct deferred_write *dw;
|
|
struct directory_entry *s_entry;
|
|
int size;
|
|
|
|
/* get the adjusted start_extent (with padding) */
|
|
/* take in accout alignment of files wrt HFS volume start */
|
|
|
|
start_extent -= session_start;
|
|
|
|
start_extent = ROUND_UP(start_extent*SECTOR_SIZE + extra*HFS_BLOCKSZ,
|
|
Csize) / SECTOR_SIZE;
|
|
|
|
start_extent -= (extra / HFS_BLK_CONV);
|
|
|
|
start_extent += session_start;
|
|
|
|
/* initialise file hash */
|
|
flush_hash();
|
|
|
|
/*
|
|
* loop through all files changing their starting blocks and finding
|
|
* any padding needed to written out latter
|
|
*/
|
|
for (dw = dw_head; dw; dw = dw->next) {
|
|
s_entry = dw->s_entry;
|
|
s_entry->starting_block = dw->extent = start_extent;
|
|
set_733((char *)s_entry->isorec.extent, start_extent);
|
|
size = ROUND_UP(dw->size, Csize) / SECTOR_SIZE;
|
|
dw->pad = size - ISO_ROUND_UP(dw->size) / SECTOR_SIZE;
|
|
|
|
/*
|
|
* cache non-HFS files - as there may be multiple links to
|
|
* these files (HFS files can't have multiple links). We will
|
|
* need to change the starting extent of the other links later
|
|
*/
|
|
if (!s_entry->hfs_ent)
|
|
add_hash(s_entry);
|
|
|
|
start_extent += size;
|
|
}
|
|
|
|
return (start_extent);
|
|
}
|
|
|
|
/*
|
|
* adj_size_other: adjust any non-HFS files that may be linked
|
|
* to an existing file (i.e. not have a deferred_write
|
|
* entry of it's own
|
|
*/
|
|
EXPORT void
|
|
adj_size_other(dpnt)
|
|
struct directory *dpnt;
|
|
{
|
|
struct directory_entry *s_entry;
|
|
struct file_hash *s_hash;
|
|
|
|
while (dpnt) {
|
|
s_entry = dpnt->contents;
|
|
for (s_entry = dpnt->contents; s_entry;
|
|
s_entry = s_entry->next) {
|
|
/*
|
|
* if it's an HFS file or a directory - then ignore
|
|
* (we're after non-HFS files)
|
|
*/
|
|
if (s_entry->hfs_ent ||
|
|
(s_entry->isorec.flags[0] & ISO_DIRECTORY))
|
|
continue;
|
|
|
|
/*
|
|
* find any cached entry and assign new starting
|
|
* extent
|
|
*/
|
|
s_hash = find_hash(s_entry);
|
|
if (s_hash) {
|
|
set_733((char *)s_entry->isorec.extent,
|
|
s_hash->starting_block);
|
|
/* not vital - but tidy */
|
|
s_entry->starting_block =
|
|
s_hash->starting_block;
|
|
}
|
|
}
|
|
if (dpnt->subdir) {
|
|
adj_size_other(dpnt->subdir);
|
|
}
|
|
dpnt = dpnt->next;
|
|
}
|
|
|
|
/* clear file hash */
|
|
flush_hash();
|
|
}
|
|
|
|
/*
|
|
* hfs_hce_write: write out the HFS header stuff
|
|
*/
|
|
LOCAL int
|
|
hfs_hce_write(outfile)
|
|
FILE *outfile;
|
|
{
|
|
char buffer[SECTOR_SIZE];
|
|
int n = 0;
|
|
int r; /* HFS hdr output */
|
|
int tot_size = hce->hfs_map_size + hce->hfs_hdr_size;
|
|
|
|
memset(buffer, 0, sizeof (buffer));
|
|
|
|
/*
|
|
* hack time ... if the tot_size is greater than 32Kb then
|
|
* it won't fit in the first 16 blank SECTORS (64 512 byte
|
|
* blocks, as most of this is padding, we just truncate this
|
|
* data to 64x4xHFS_BLOCKSZ ... hope this is OK ...
|
|
*/
|
|
|
|
if (tot_size > 64) tot_size = 64;
|
|
|
|
/* get size in CD blocks == 4xHFS_BLOCKSZ == 2048 */
|
|
n = tot_size / HFS_BLK_CONV;
|
|
r = tot_size % HFS_BLK_CONV;
|
|
|
|
/* write out HFS volume header info */
|
|
xfwrite(hce->hfs_map, HFS_BLOCKSZ, tot_size, outfile, 0, FALSE);
|
|
|
|
/* fill up to a complete CD block */
|
|
if (r) {
|
|
xfwrite(buffer, HFS_BLOCKSZ, HFS_BLK_CONV - r, outfile, 0, FALSE);
|
|
n++;
|
|
}
|
|
last_extent_written += n;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* insert_padding_file : insert a dumy file to make volume at least
|
|
* 800k
|
|
*
|
|
* XXX If we ever need to write more then 2 GB, make size off_t
|
|
*/
|
|
EXPORT int
|
|
insert_padding_file(size)
|
|
int size;
|
|
{
|
|
struct deferred_write *dwpnt;
|
|
|
|
/* get the size in bytes */
|
|
size *= HFS_BLOCKSZ;
|
|
|
|
dwpnt = (struct deferred_write *)
|
|
e_malloc(sizeof (struct deferred_write));
|
|
dwpnt->s_entry = 0;
|
|
/* set the padding to zero */
|
|
dwpnt->pad = 0;
|
|
/* set offset to zero */
|
|
dwpnt->off = (off_t)0;
|
|
dwpnt->dw_flags = 0;
|
|
#ifdef APPLE_HYB
|
|
dwpnt->hfstype = TYPE_NONE;
|
|
#endif
|
|
|
|
/*
|
|
* don't need to wory about the s_entry stuff as it won't be touched#
|
|
* at this point onwards
|
|
*/
|
|
|
|
/* insert the entry in the list */
|
|
if (dw_tail) {
|
|
dw_tail->next = dwpnt;
|
|
dw_tail = dwpnt;
|
|
} else {
|
|
dw_head = dwpnt;
|
|
dw_tail = dwpnt;
|
|
}
|
|
|
|
/* aloocate memory as a "Table" file */
|
|
dwpnt->table = e_malloc(size);
|
|
dwpnt->name = NULL;
|
|
|
|
dwpnt->next = NULL;
|
|
dwpnt->size = size;
|
|
dwpnt->extent = last_extent;
|
|
last_extent += ISO_BLOCKS(size);
|
|
|
|
/* retune the size in HFS blocks */
|
|
return (ISO_ROUND_UP(size) / HFS_BLOCKSZ);
|
|
}
|
|
|
|
struct output_fragment hfs_desc = {NULL, NULL, NULL, hfs_hce_write, "HFS volume header"};
|
|
|
|
#endif /* APPLE_HYB */
|
|
|
|
struct output_fragment startpad_desc = {NULL, startpad_size, NULL, startpad_write, "Initial Padblock"};
|
|
struct output_fragment voldesc_desc = {NULL, oneblock_size, root_gen, pvd_write, "Primary Volume Descriptor"};
|
|
struct output_fragment xvoldesc_desc = {NULL, oneblock_size, NULL, xpvd_write, "Enhanced Volume Descriptor"};
|
|
struct output_fragment end_vol = {NULL, oneblock_size, NULL, evd_write, "End Volume Descriptor" };
|
|
struct output_fragment version_desc = {NULL, oneblock_size, NULL, vers_write, "Version block" };
|
|
struct output_fragment pathtable_desc = {NULL, pathtab_size, generate_path_tables, pathtab_write, "Path table"};
|
|
struct output_fragment dirtree_desc = {NULL, dirtree_size, NULL, dirtree_write, "Directory tree" };
|
|
struct output_fragment dirtree_clean = {NULL, dirtree_fixup, dirtree_dump, dirtree_cleanup, "Directory tree cleanup" };
|
|
struct output_fragment extension_desc = {NULL, ext_size, NULL, exten_write, "Extension record" };
|
|
struct output_fragment files_desc = {NULL, NULL, file_gen, file_write, "The File(s)"};
|
|
struct output_fragment interpad_desc = {NULL, interpad_size, NULL, interpad_write, "Intermediate Padblock"};
|
|
struct output_fragment endpad_desc = {NULL, endpad_size, NULL, endpad_write, "Ending Padblock"};
|