cdrtools/mkisofs/inode.c
2025-06-15 04:19:58 +08:00

448 lines
11 KiB
C

/* @(#)inode.c 1.19 16/11/14 Copyright 2006-2016 J. Schilling */
#include <schily/mconfig.h>
#ifndef lint
static UConst char sccsid[] =
"@(#)inode.c 1.19 16/11/14 Copyright 2006-2015 J. Schilling";
#endif
/*
* Inode and link count handling for ISO-9660/RR
*
* This module computes and sets up a RR link count that reflects
* the name-count for files/directories in the ISO-9660/RR image.
* This module also assigns inode numbers tp all files/directories
* using either the RRip-112 protocol or a mkisofs specific method
* of asigning the related number to the "extent" field in the ISO
* directory record.
*
* Copyright (c) 2006-2016 J. Schilling
*/
/*@@C@@*/
#include "mkisofs.h"
#include <schily/schily.h>
/*
* Highest inode value we assign in this session.
*/
LOCAL UInt32_t null_ino_high;
EXPORT void do_inode __PR((struct directory *dpnt));
EXPORT void do_dir_nlink __PR((struct directory *dpnt));
LOCAL void assign_inodes __PR((struct directory *dpnt));
LOCAL void compute_linkcount __PR((struct directory *dpnt));
LOCAL void assign_linkcount __PR((struct directory *dpnt));
LOCAL void update_inode __PR((struct directory_entry *s_entry, int value));
LOCAL void update_nlink __PR((struct directory_entry *s_entry, int value));
LOCAL int update_dir_nlink __PR((struct directory *dpnt));
/*
* Inode/hard link related stuff for non-directory type files.
*/
EXPORT void
do_inode(dpnt)
struct directory *dpnt;
{
null_ino_high = null_inodes;
if (correct_inodes)
assign_inodes(root);
#ifdef UDF
if (!use_RockRidge && !use_udf)
return;
#else
if (!use_RockRidge)
return;
#endif
if (!cache_inodes) /* Never FALSE if correct_inodes TRUE */
return;
compute_linkcount(dpnt);
if (use_RockRidge) /* If we have Rock Ridge extensions, */
assign_linkcount(dpnt); /* reassign computed linkcount in RR */
if (null_inodes < last_extent)
comerrno(EX_BAD, _("Inode number overflow, too many files in file system.\n"));
}
/*
* Set the link count for directories to 2 + number of sub-directories.
*/
EXPORT void
do_dir_nlink(dpnt)
struct directory *dpnt;
{
int rootlinks;
if (!use_RockRidge)
return;
/*
* Update everything except "/..".
*/
rootlinks = update_dir_nlink(dpnt);
if (reloc_dir)
rootlinks--; /* rr_moved is hidden */
/*
* Update "/." now.
*/
update_nlink(dpnt->contents, rootlinks);
/*
* Update "/.." now.
*/
update_nlink(dpnt->contents->next, rootlinks);
}
/*
* Assign inode numbers to files of zero size and to symlinks.
*/
LOCAL void
assign_inodes(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 (s_entry->starting_block == 0) {
s_hash = find_hash(s_entry);
/* find_directory_hash() ? */
if (s_hash)
s_entry->starting_block = s_hash->starting_block;
}
if (s_entry->starting_block == 0 && s_entry->size != 0) {
unsigned int e = get_733((char *)s_entry->isorec.extent);
if (e != 0) {
errmsgno(EX_BAD,
_("Implementation botch, fetching extend %d for %s from dir entry.\n"),
e, s_entry->whole_name);
}
}
if (use_RockRidge && s_entry->starting_block > 0)
update_inode(s_entry, s_entry->starting_block);
/*
* Be careful: UDF Symlinks have size != 0, then
* s_hash->starting_block is a valid inode number.
*/
if (s_entry->size != 0)
continue;
#ifdef UDF
if ((s_entry->de_flags & IS_SYMLINK) != 0 &&
create_udfsymlinks)
continue;
#else
if ((s_entry->de_flags & IS_SYMLINK) != 0)
continue;
#endif
if (s_entry->isorec.flags[0] & ISO_DIRECTORY)
continue;
/*
* Assign inodes to symbolic links.
*/
if (s_entry->dev == UNCACHED_DEVICE && s_entry->inode == UNCACHED_INODE) {
s_entry->dev = PREV_SESS_DEV;
s_entry->inode = null_inodes;
}
s_hash = find_hash(s_entry);
if (s_hash) {
/*
* Paranoia: Check for hashed files without proper inode #.
*/
if (s_hash->starting_block <= last_extent)
comerrno(EX_BAD,
_("Implementation botch: Hashed file '%s' has illegal inode %u.\n"),
s_entry->whole_name ?
s_entry->whole_name : s_entry->name,
s_hash->starting_block);
set_733((char *)s_entry->isorec.extent, s_hash->starting_block);
s_entry->starting_block = s_hash->starting_block;
} else {
s_entry->starting_block = null_inodes--;
set_733((char *)s_entry->isorec.extent, s_entry->starting_block);
add_hash(s_entry);
}
if (use_RockRidge)
update_inode(s_entry, s_entry->starting_block);
}
if (dpnt->subdir) {
assign_inodes(dpnt->subdir);
}
dpnt = dpnt->next;
}
}
/*
* Compute the link count for non-directory type files.
*/
LOCAL void
compute_linkcount(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) {
/*
* Skip directories.
*/
if (s_entry->isorec.flags[0] & ISO_DIRECTORY)
continue;
if (s_entry->de_flags & RELOCATED_DIRECTORY)
continue;
/*
* skip resource files or file stream files
* XXX should we assign a standard link count == 1 instead?
*/
if (s_entry->de_flags & RESOURCE_FORK)
continue;
/*
* Assign inodes to symbolic links.
* We never come here in case that we create correct inodes,
* except with UDF symlinks.
*/
if (s_entry->dev == UNCACHED_DEVICE && s_entry->inode == UNCACHED_INODE) {
s_entry->dev = PREV_SESS_DEV;
/*
* With UDF symlinks, the starting_block is a
* valid inode number.
*/
#ifdef UDF
if ((s_entry->de_flags & IS_SYMLINK) != 0 &&
create_udfsymlinks) {
#else
if ((s_entry->de_flags & IS_SYMLINK) != 0) {
#endif
s_entry->inode = s_entry->starting_block;
} else {
s_entry->inode = null_inodes--; /* Only used for caching */
if (correct_inodes)
comerrno(EX_BAD,
_("Implementation botch: Unhashed file '%s'.\n"),
s_entry->whole_name ?
s_entry->whole_name : s_entry->name);
}
}
s_hash = find_hash(s_entry);
if (s_hash) {
s_hash->nlink++;
} else {
add_hash(s_entry);
s_hash = find_hash(s_entry);
if (s_hash == NULL) {
if (s_entry->dev == UNCACHED_DEVICE &&
s_entry->inode == TABLE_INODE) {
continue;
}
comerrno(EX_BAD,
_("Implementation botch: File '%s' not hashed (dev/ino %llX/%llX).\n"),
s_entry->whole_name ?
s_entry->whole_name : s_entry->name,
(Llong)s_entry->dev,
(Llong)s_entry->inode);
}
s_hash->nlink++;
}
}
if (dpnt->subdir) {
compute_linkcount(dpnt->subdir);
}
dpnt = dpnt->next;
}
}
/*
* Assig the link count for non-directory type files to the value
* computed with compute_linkcount().
*/
LOCAL void
assign_linkcount(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 (s_entry->isorec.flags[0] & ISO_DIRECTORY)
continue;
if (s_entry->de_flags & RELOCATED_DIRECTORY)
continue;
/*
* skip resource files or file stream files
*/
if (s_entry->de_flags & RESOURCE_FORK)
continue;
s_hash = find_hash(s_entry);
if (s_hash) {
update_nlink(s_entry, s_hash->nlink);
} else {
if (s_entry->dev == UNCACHED_DEVICE &&
s_entry->inode == TABLE_INODE) {
continue;
}
comerrno(EX_BAD,
_("Implementation botch: File '%s' not hashed.\n"),
s_entry->whole_name ?
s_entry->whole_name : s_entry->name);
}
}
if (dpnt->subdir) {
assign_linkcount(dpnt->subdir);
}
dpnt = dpnt->next;
}
}
/*
* Rewrite the content of the RR inode field in the PX record.
*/
LOCAL void
update_inode(s_entry, value)
struct directory_entry *s_entry;
int value;
{
unsigned char *pnt;
int len;
if (!rrip112)
return;
pnt = s_entry->rr_attributes;
len = s_entry->total_rr_attr_size;
pnt = parse_xa(pnt, &len, 0);
while (len >= 4) {
if (pnt[3] != 1 && pnt[3] != 2) {
errmsgno(EX_BAD,
_("**BAD RRVERSION (%d) in '%c%c' field (%2.2X %2.2X).\n"),
pnt[3], pnt[0], pnt[1], pnt[0], pnt[1]);
}
if (pnt[2] < 4) {
errmsgno(EX_BAD,
_("**BAD RRLEN (%d) in '%2.2s' field %2.2X %2.2X.\n"),
pnt[2], pnt, pnt[0], pnt[1]);
break;
}
if (pnt[0] == 'P' && pnt[1] == 'X') {
if ((pnt[2] & 0xFF) < 44) /* Paranoia */
return;
set_733((char *)pnt + 36, value);
break;
}
len -= pnt[2];
pnt += pnt[2];
}
}
/*
* Rewrite the content of the RR nlink field in the PX record.
*/
LOCAL void
update_nlink(s_entry, value)
struct directory_entry *s_entry;
int value;
{
unsigned char *pnt;
int len;
pnt = s_entry->rr_attributes;
len = s_entry->total_rr_attr_size;
pnt = parse_xa(pnt, &len, 0);
while (len >= 4) {
if (pnt[3] != 1 && pnt[3] != 2) {
errmsgno(EX_BAD,
_("**BAD RRVERSION (%d) in '%c%c' field (%2.2X %2.2X).\n"),
pnt[3], pnt[0], pnt[1], pnt[0], pnt[1]);
}
if (pnt[2] < 4) {
errmsgno(EX_BAD,
_("**BAD RRLEN (%d) in '%2.2s' field %2.2X %2.2X.\n"),
pnt[2], pnt, pnt[0], pnt[1]);
break;
}
if (pnt[0] == 'P' && pnt[1] == 'X') {
set_733((char *)pnt + 12, value);
break;
}
len -= pnt[2];
pnt += pnt[2];
}
}
/*
* Set the link count for directories to 2 + number of sub-directories.
* This is done here for all diresctories except for "/..".
*/
LOCAL int
update_dir_nlink(dpnt)
struct directory *dpnt;
{
struct directory *xpnt;
struct directory_entry *s_entry;
int i = 0;
while (dpnt) {
if (dpnt->dir_flags & INHIBIT_ISO9660_ENTRY) {
dpnt = dpnt->next;
continue;
}
/*
* First, count up the number of subdirectories this dir has.
*/
for (i = 0, xpnt = dpnt->subdir; xpnt; xpnt = xpnt->next)
if ((xpnt->dir_flags & INHIBIT_ISO9660_ENTRY) == 0)
i++;
/*
* Next check to see if we have any relocated directories in
* this directory. The nlink field will include these as
* real directories when they are properly relocated.
* In the non-rockridge disk, the relocated entries appear as
* zero length files.
*/
for (s_entry = dpnt->contents; s_entry;
s_entry = s_entry->next) {
if ((s_entry->de_flags & RELOCATED_DIRECTORY) != 0 &&
(s_entry->de_flags & INHIBIT_ISO9660_ENTRY) ==
0) {
i++;
}
}
/*
* Now update the field in the Rock Ridge entry.
*/
update_nlink(dpnt->self, i + 2);
/*
* Update the '.' entry for this directory.
*/
update_nlink(dpnt->contents, i + 2);
/*
* Update all of the '..' entries that point to this guy.
*/
for (xpnt = dpnt->subdir; xpnt; xpnt = xpnt->next) {
update_nlink(xpnt->contents->next, i + 2);
}
if (dpnt->subdir)
update_dir_nlink(dpnt->subdir);
dpnt = dpnt->next;
}
return (i+2);
}