448 lines
11 KiB
C
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);
|
||
|
}
|