Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+19
View File
@@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-2.0-only
config UDF_FS
tristate "UDF file system support"
select BUFFER_HEAD
select CRC_ITU_T
select NLS
select LEGACY_DIRECT_IO
help
This is a file system used on some CD-ROMs and DVDs. Since the
file system is supported by multiple operating systems and is more
compatible with standard unix file systems, it is also suitable for
removable USB disks. Say Y if you intend to mount DVD discs or CDRW's
written in packet mode, or if you want to use UDF for removable USB
disks. Please read <file:Documentation/filesystems/udf.rst>.
To compile this file system support as a module, choose M here: the
module will be called udf.
If unsure, say N.
+10
View File
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for the linux udf-filesystem routines.
#
obj-$(CONFIG_UDF_FS) += udf.o
udf-objs := balloc.o dir.o file.o ialloc.o inode.o lowlevel.o namei.o \
partition.o super.o truncate.o symlink.o \
directory.o misc.o udftime.o unicode.o
+736
View File
@@ -0,0 +1,736 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* balloc.c
*
* PURPOSE
* Block allocation handling routines for the OSTA-UDF(tm) filesystem.
*
* COPYRIGHT
* (C) 1999-2001 Ben Fennema
* (C) 1999 Stelias Computing Inc
*
* HISTORY
*
* 02/24/99 blf Created.
*
*/
#include "udfdecl.h"
#include <linux/bitops.h>
#include <linux/overflow.h>
#include "udf_i.h"
#include "udf_sb.h"
#define udf_clear_bit __test_and_clear_bit_le
#define udf_set_bit __test_and_set_bit_le
#define udf_test_bit test_bit_le
#define udf_find_next_one_bit find_next_bit_le
static int read_block_bitmap(struct super_block *sb,
struct udf_bitmap *bitmap, unsigned int block,
unsigned long bitmap_nr)
{
struct buffer_head *bh = NULL;
int i;
int max_bits, off, count;
struct kernel_lb_addr loc;
loc.logicalBlockNum = bitmap->s_extPosition;
loc.partitionReferenceNum = UDF_SB(sb)->s_partition;
bh = sb_bread(sb, udf_get_lb_pblock(sb, &loc, block));
bitmap->s_block_bitmap[bitmap_nr] = bh;
if (!bh)
return -EIO;
/* Check consistency of Space Bitmap buffer. */
max_bits = sb->s_blocksize * 8;
if (!bitmap_nr) {
off = sizeof(struct spaceBitmapDesc) << 3;
count = min(max_bits - off, bitmap->s_nr_groups);
} else {
/*
* Rough check if bitmap number is too big to have any bitmap
* blocks reserved.
*/
if (bitmap_nr >
(bitmap->s_nr_groups >> (sb->s_blocksize_bits + 3)) + 2)
return 0;
off = 0;
count = bitmap->s_nr_groups - bitmap_nr * max_bits +
(sizeof(struct spaceBitmapDesc) << 3);
count = min(count, max_bits);
}
for (i = 0; i < count; i++)
if (udf_test_bit(i + off, bh->b_data)) {
bitmap->s_block_bitmap[bitmap_nr] =
ERR_PTR(-EFSCORRUPTED);
brelse(bh);
return -EFSCORRUPTED;
}
return 0;
}
static int load_block_bitmap(struct super_block *sb,
struct udf_bitmap *bitmap,
unsigned int block_group)
{
int retval = 0;
int nr_groups = bitmap->s_nr_groups;
if (block_group >= nr_groups) {
udf_debug("block_group (%u) >= nr_groups (%d)\n",
block_group, nr_groups);
return -EFSCORRUPTED;
}
if (bitmap->s_block_bitmap[block_group]) {
/*
* The bitmap failed verification in the past. No point in
* trying again.
*/
if (IS_ERR(bitmap->s_block_bitmap[block_group]))
return PTR_ERR(bitmap->s_block_bitmap[block_group]);
return block_group;
}
retval = read_block_bitmap(sb, bitmap, block_group, block_group);
if (retval < 0)
return retval;
return block_group;
}
static void udf_add_free_space(struct super_block *sb, u16 partition, u32 cnt)
{
struct udf_sb_info *sbi = UDF_SB(sb);
struct logicalVolIntegrityDesc *lvid;
if (!sbi->s_lvid_bh)
return;
lvid = (struct logicalVolIntegrityDesc *)sbi->s_lvid_bh->b_data;
le32_add_cpu(&lvid->freeSpaceTable[partition], cnt);
udf_updated_lvid(sb);
}
static void udf_bitmap_free_blocks(struct super_block *sb,
struct udf_bitmap *bitmap,
struct kernel_lb_addr *bloc,
uint32_t offset,
uint32_t count)
{
struct udf_sb_info *sbi = UDF_SB(sb);
struct buffer_head *bh = NULL;
unsigned long block;
unsigned long block_group;
unsigned long bit;
unsigned long i;
int bitmap_nr;
unsigned long overflow;
mutex_lock(&sbi->s_alloc_mutex);
/* We make sure this cannot overflow when mounting the filesystem */
block = bloc->logicalBlockNum + offset +
(sizeof(struct spaceBitmapDesc) << 3);
do {
overflow = 0;
block_group = block >> (sb->s_blocksize_bits + 3);
bit = block % (sb->s_blocksize << 3);
/*
* Check to see if we are freeing blocks across a group boundary.
*/
if (bit + count > (sb->s_blocksize << 3)) {
overflow = bit + count - (sb->s_blocksize << 3);
count -= overflow;
}
bitmap_nr = load_block_bitmap(sb, bitmap, block_group);
if (bitmap_nr < 0)
goto error_return;
bh = bitmap->s_block_bitmap[bitmap_nr];
for (i = 0; i < count; i++) {
if (udf_set_bit(bit + i, bh->b_data)) {
udf_debug("bit %lu already set\n", bit + i);
udf_debug("byte=%2x\n",
((__u8 *)bh->b_data)[(bit + i) >> 3]);
}
}
udf_add_free_space(sb, sbi->s_partition, count);
mark_buffer_dirty(bh);
if (overflow) {
block += count;
count = overflow;
}
} while (overflow);
error_return:
mutex_unlock(&sbi->s_alloc_mutex);
}
static int udf_bitmap_prealloc_blocks(struct super_block *sb,
struct udf_bitmap *bitmap,
uint16_t partition, uint32_t first_block,
uint32_t block_count)
{
struct udf_sb_info *sbi = UDF_SB(sb);
int alloc_count = 0;
int bit, block, block_group;
int bitmap_nr;
struct buffer_head *bh;
__u32 part_len;
mutex_lock(&sbi->s_alloc_mutex);
part_len = sbi->s_partmaps[partition].s_partition_len;
if (first_block >= part_len)
goto out;
if (first_block + block_count > part_len)
block_count = part_len - first_block;
do {
block = first_block + (sizeof(struct spaceBitmapDesc) << 3);
block_group = block >> (sb->s_blocksize_bits + 3);
bitmap_nr = load_block_bitmap(sb, bitmap, block_group);
if (bitmap_nr < 0)
goto out;
bh = bitmap->s_block_bitmap[bitmap_nr];
bit = block % (sb->s_blocksize << 3);
while (bit < (sb->s_blocksize << 3) && block_count > 0) {
if (!udf_clear_bit(bit, bh->b_data))
goto out;
block_count--;
alloc_count++;
bit++;
block++;
}
mark_buffer_dirty(bh);
} while (block_count > 0);
out:
udf_add_free_space(sb, partition, -alloc_count);
mutex_unlock(&sbi->s_alloc_mutex);
return alloc_count;
}
static udf_pblk_t udf_bitmap_new_block(struct super_block *sb,
struct udf_bitmap *bitmap, uint16_t partition,
uint32_t goal, int *err)
{
struct udf_sb_info *sbi = UDF_SB(sb);
int newbit, bit = 0;
udf_pblk_t block;
int block_group, group_start;
int end_goal, nr_groups, bitmap_nr, i;
struct buffer_head *bh = NULL;
char *ptr;
udf_pblk_t newblock = 0;
*err = -ENOSPC;
mutex_lock(&sbi->s_alloc_mutex);
repeat:
if (goal >= sbi->s_partmaps[partition].s_partition_len)
goal = 0;
nr_groups = bitmap->s_nr_groups;
block = goal + (sizeof(struct spaceBitmapDesc) << 3);
block_group = block >> (sb->s_blocksize_bits + 3);
group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc);
bitmap_nr = load_block_bitmap(sb, bitmap, block_group);
if (bitmap_nr < 0)
goto error_return;
bh = bitmap->s_block_bitmap[bitmap_nr];
ptr = memscan((char *)bh->b_data + group_start, 0xFF,
sb->s_blocksize - group_start);
if ((ptr - ((char *)bh->b_data)) < sb->s_blocksize) {
bit = block % (sb->s_blocksize << 3);
if (udf_test_bit(bit, bh->b_data))
goto got_block;
end_goal = (bit + 63) & ~63;
bit = udf_find_next_one_bit(bh->b_data, end_goal, bit);
if (bit < end_goal)
goto got_block;
ptr = memscan((char *)bh->b_data + (bit >> 3), 0xFF,
sb->s_blocksize - ((bit + 7) >> 3));
newbit = (ptr - ((char *)bh->b_data)) << 3;
if (newbit < sb->s_blocksize << 3) {
bit = newbit;
goto search_back;
}
newbit = udf_find_next_one_bit(bh->b_data,
sb->s_blocksize << 3, bit);
if (newbit < sb->s_blocksize << 3) {
bit = newbit;
goto got_block;
}
}
for (i = 0; i < (nr_groups * 2); i++) {
block_group++;
if (block_group >= nr_groups)
block_group = 0;
group_start = block_group ? 0 : sizeof(struct spaceBitmapDesc);
bitmap_nr = load_block_bitmap(sb, bitmap, block_group);
if (bitmap_nr < 0)
goto error_return;
bh = bitmap->s_block_bitmap[bitmap_nr];
if (i < nr_groups) {
ptr = memscan((char *)bh->b_data + group_start, 0xFF,
sb->s_blocksize - group_start);
if ((ptr - ((char *)bh->b_data)) < sb->s_blocksize) {
bit = (ptr - ((char *)bh->b_data)) << 3;
break;
}
} else {
bit = udf_find_next_one_bit(bh->b_data,
sb->s_blocksize << 3,
group_start << 3);
if (bit < sb->s_blocksize << 3)
break;
}
}
if (i >= (nr_groups * 2)) {
mutex_unlock(&sbi->s_alloc_mutex);
return newblock;
}
if (bit < sb->s_blocksize << 3)
goto search_back;
else
bit = udf_find_next_one_bit(bh->b_data, sb->s_blocksize << 3,
group_start << 3);
if (bit >= sb->s_blocksize << 3) {
mutex_unlock(&sbi->s_alloc_mutex);
return 0;
}
search_back:
i = 0;
while (i < 7 && bit > (group_start << 3) &&
udf_test_bit(bit - 1, bh->b_data)) {
++i;
--bit;
}
got_block:
newblock = bit + (block_group << (sb->s_blocksize_bits + 3)) -
(sizeof(struct spaceBitmapDesc) << 3);
if (newblock >= sbi->s_partmaps[partition].s_partition_len) {
/*
* Ran off the end of the bitmap, and bits following are
* non-compliant (not all zero)
*/
udf_err(sb, "bitmap for partition %d corrupted (block %u marked"
" as free, partition length is %u)\n", partition,
newblock, sbi->s_partmaps[partition].s_partition_len);
goto error_return;
}
if (!udf_clear_bit(bit, bh->b_data)) {
udf_debug("bit already cleared for block %d\n", bit);
goto repeat;
}
mark_buffer_dirty(bh);
udf_add_free_space(sb, partition, -1);
mutex_unlock(&sbi->s_alloc_mutex);
*err = 0;
return newblock;
error_return:
*err = -EIO;
mutex_unlock(&sbi->s_alloc_mutex);
return 0;
}
static void udf_table_free_blocks(struct super_block *sb,
struct inode *table,
struct kernel_lb_addr *bloc,
uint32_t offset,
uint32_t count)
{
struct udf_sb_info *sbi = UDF_SB(sb);
uint32_t start, end;
uint32_t elen;
struct kernel_lb_addr eloc;
struct extent_position oepos, epos;
int8_t etype;
struct udf_inode_info *iinfo;
int ret = 0;
mutex_lock(&sbi->s_alloc_mutex);
iinfo = UDF_I(table);
udf_add_free_space(sb, sbi->s_partition, count);
start = bloc->logicalBlockNum + offset;
end = bloc->logicalBlockNum + offset + count - 1;
epos.offset = oepos.offset = sizeof(struct unallocSpaceEntry);
elen = 0;
epos.block = oepos.block = iinfo->i_location;
epos.bh = oepos.bh = NULL;
while (count) {
ret = udf_next_aext(table, &epos, &eloc, &elen, &etype, 1);
if (ret < 0)
goto error_return;
if (ret == 0)
break;
if (((eloc.logicalBlockNum +
(elen >> sb->s_blocksize_bits)) == start)) {
if ((0x3FFFFFFF - elen) <
(count << sb->s_blocksize_bits)) {
uint32_t tmp = ((0x3FFFFFFF - elen) >>
sb->s_blocksize_bits);
count -= tmp;
start += tmp;
elen = (etype << 30) |
(0x40000000 - sb->s_blocksize);
} else {
elen = (etype << 30) |
(elen +
(count << sb->s_blocksize_bits));
start += count;
count = 0;
}
udf_write_aext(table, &oepos, &eloc, elen, 1);
} else if (eloc.logicalBlockNum == (end + 1)) {
if ((0x3FFFFFFF - elen) <
(count << sb->s_blocksize_bits)) {
uint32_t tmp = ((0x3FFFFFFF - elen) >>
sb->s_blocksize_bits);
count -= tmp;
end -= tmp;
eloc.logicalBlockNum -= tmp;
elen = (etype << 30) |
(0x40000000 - sb->s_blocksize);
} else {
eloc.logicalBlockNum = start;
elen = (etype << 30) |
(elen +
(count << sb->s_blocksize_bits));
end -= count;
count = 0;
}
udf_write_aext(table, &oepos, &eloc, elen, 1);
}
if (epos.bh != oepos.bh) {
oepos.block = epos.block;
brelse(oepos.bh);
get_bh(epos.bh);
oepos.bh = epos.bh;
oepos.offset = 0;
} else {
oepos.offset = epos.offset;
}
}
if (count) {
/*
* NOTE: we CANNOT use udf_add_aext here, as it can try to
* allocate a new block, and since we hold the super block
* lock already very bad things would happen :)
*
* We copy the behavior of udf_add_aext, but instead of
* trying to allocate a new block close to the existing one,
* we just steal a block from the extent we are trying to add.
*
* It would be nice if the blocks were close together, but it
* isn't required.
*/
int adsize;
eloc.logicalBlockNum = start;
elen = EXT_RECORDED_ALLOCATED |
(count << sb->s_blocksize_bits);
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
adsize = sizeof(struct short_ad);
else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
adsize = sizeof(struct long_ad);
else
goto error_return;
if (epos.offset + (2 * adsize) > sb->s_blocksize) {
/* Steal a block from the extent being free'd */
udf_setup_indirect_aext(table, eloc.logicalBlockNum,
&epos);
eloc.logicalBlockNum++;
elen -= sb->s_blocksize;
}
/* It's possible that stealing the block emptied the extent */
if (elen)
__udf_add_aext(table, &epos, &eloc, elen, 1);
}
error_return:
brelse(epos.bh);
brelse(oepos.bh);
mutex_unlock(&sbi->s_alloc_mutex);
return;
}
static int udf_table_prealloc_blocks(struct super_block *sb,
struct inode *table, uint16_t partition,
uint32_t first_block, uint32_t block_count)
{
struct udf_sb_info *sbi = UDF_SB(sb);
int alloc_count = 0;
uint32_t elen, adsize;
struct kernel_lb_addr eloc;
struct extent_position epos;
int8_t etype = -1;
struct udf_inode_info *iinfo;
int ret = 0;
if (first_block >= sbi->s_partmaps[partition].s_partition_len)
return 0;
iinfo = UDF_I(table);
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
adsize = sizeof(struct short_ad);
else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
adsize = sizeof(struct long_ad);
else
return 0;
mutex_lock(&sbi->s_alloc_mutex);
epos.offset = sizeof(struct unallocSpaceEntry);
epos.block = iinfo->i_location;
epos.bh = NULL;
eloc.logicalBlockNum = 0xFFFFFFFF;
while (first_block != eloc.logicalBlockNum) {
ret = udf_next_aext(table, &epos, &eloc, &elen, &etype, 1);
if (ret < 0)
goto err_out;
if (ret == 0)
break;
udf_debug("eloc=%u, elen=%u, first_block=%u\n",
eloc.logicalBlockNum, elen, first_block);
}
if (first_block == eloc.logicalBlockNum) {
epos.offset -= adsize;
alloc_count = (elen >> sb->s_blocksize_bits);
if (alloc_count > block_count) {
alloc_count = block_count;
eloc.logicalBlockNum += alloc_count;
elen -= (alloc_count << sb->s_blocksize_bits);
udf_write_aext(table, &epos, &eloc,
(etype << 30) | elen, 1);
} else
udf_delete_aext(table, epos);
} else {
alloc_count = 0;
}
err_out:
brelse(epos.bh);
if (alloc_count)
udf_add_free_space(sb, partition, -alloc_count);
mutex_unlock(&sbi->s_alloc_mutex);
return alloc_count;
}
static udf_pblk_t udf_table_new_block(struct super_block *sb,
struct inode *table, uint16_t partition,
uint32_t goal, int *err)
{
struct udf_sb_info *sbi = UDF_SB(sb);
uint32_t spread = 0xFFFFFFFF, nspread = 0xFFFFFFFF;
udf_pblk_t newblock = 0;
uint32_t adsize;
uint32_t elen, goal_elen = 0;
struct kernel_lb_addr eloc, goal_eloc;
struct extent_position epos, goal_epos;
int8_t etype;
struct udf_inode_info *iinfo = UDF_I(table);
int ret = 0;
*err = -ENOSPC;
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
adsize = sizeof(struct short_ad);
else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
adsize = sizeof(struct long_ad);
else
return newblock;
mutex_lock(&sbi->s_alloc_mutex);
if (goal >= sbi->s_partmaps[partition].s_partition_len)
goal = 0;
/* We search for the closest matching block to goal. If we find
a exact hit, we stop. Otherwise we keep going till we run out
of extents. We store the buffer_head, bloc, and extoffset
of the current closest match and use that when we are done.
*/
epos.offset = sizeof(struct unallocSpaceEntry);
epos.block = iinfo->i_location;
epos.bh = goal_epos.bh = NULL;
while (spread) {
ret = udf_next_aext(table, &epos, &eloc, &elen, &etype, 1);
if (ret <= 0)
break;
if (goal >= eloc.logicalBlockNum) {
if (goal < eloc.logicalBlockNum +
(elen >> sb->s_blocksize_bits))
nspread = 0;
else
nspread = goal - eloc.logicalBlockNum -
(elen >> sb->s_blocksize_bits);
} else {
nspread = eloc.logicalBlockNum - goal;
}
if (nspread < spread) {
spread = nspread;
if (goal_epos.bh != epos.bh) {
brelse(goal_epos.bh);
goal_epos.bh = epos.bh;
get_bh(goal_epos.bh);
}
goal_epos.block = epos.block;
goal_epos.offset = epos.offset - adsize;
goal_eloc = eloc;
goal_elen = (etype << 30) | elen;
}
}
brelse(epos.bh);
if (ret < 0 || spread == 0xFFFFFFFF) {
brelse(goal_epos.bh);
mutex_unlock(&sbi->s_alloc_mutex);
if (ret < 0)
*err = ret;
return 0;
}
/* Only allocate blocks from the beginning of the extent.
That way, we only delete (empty) extents, never have to insert an
extent because of splitting */
/* This works, but very poorly.... */
newblock = goal_eloc.logicalBlockNum;
goal_eloc.logicalBlockNum++;
goal_elen -= sb->s_blocksize;
if (goal_elen)
udf_write_aext(table, &goal_epos, &goal_eloc, goal_elen, 1);
else
udf_delete_aext(table, goal_epos);
brelse(goal_epos.bh);
udf_add_free_space(sb, partition, -1);
mutex_unlock(&sbi->s_alloc_mutex);
*err = 0;
return newblock;
}
void udf_free_blocks(struct super_block *sb, struct inode *inode,
struct kernel_lb_addr *bloc, uint32_t offset,
uint32_t count)
{
uint16_t partition = bloc->partitionReferenceNum;
struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition];
uint32_t blk;
if (check_add_overflow(bloc->logicalBlockNum, offset, &blk) ||
check_add_overflow(blk, count, &blk) ||
blk > map->s_partition_len) {
udf_debug("Invalid request to free blocks: (%d, %u), off %u, "
"len %u, partition len %u\n",
partition, bloc->logicalBlockNum, offset, count,
map->s_partition_len);
return;
}
if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP) {
udf_bitmap_free_blocks(sb, map->s_uspace.s_bitmap,
bloc, offset, count);
} else if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_TABLE) {
udf_table_free_blocks(sb, map->s_uspace.s_table,
bloc, offset, count);
}
if (inode) {
inode_sub_bytes(inode,
((sector_t)count) << sb->s_blocksize_bits);
}
}
inline int udf_prealloc_blocks(struct super_block *sb,
struct inode *inode,
uint16_t partition, uint32_t first_block,
uint32_t block_count)
{
struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition];
int allocated;
if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP)
allocated = udf_bitmap_prealloc_blocks(sb,
map->s_uspace.s_bitmap,
partition, first_block,
block_count);
else if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_TABLE)
allocated = udf_table_prealloc_blocks(sb,
map->s_uspace.s_table,
partition, first_block,
block_count);
else
return 0;
if (inode && allocated > 0)
inode_add_bytes(inode, allocated << sb->s_blocksize_bits);
return allocated;
}
inline udf_pblk_t udf_new_block(struct super_block *sb,
struct inode *inode,
uint16_t partition, uint32_t goal, int *err)
{
struct udf_part_map *map = &UDF_SB(sb)->s_partmaps[partition];
udf_pblk_t block;
if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_BITMAP)
block = udf_bitmap_new_block(sb,
map->s_uspace.s_bitmap,
partition, goal, err);
else if (map->s_partition_flags & UDF_PART_FLAG_UNALLOC_TABLE)
block = udf_table_new_block(sb,
map->s_uspace.s_table,
partition, goal, err);
else {
*err = -EIO;
return 0;
}
if (inode && block)
inode_add_bytes(inode, sb->s_blocksize);
return block;
}
+162
View File
@@ -0,0 +1,162 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* dir.c
*
* PURPOSE
* Directory handling routines for the OSTA-UDF(tm) filesystem.
*
* COPYRIGHT
* (C) 1998-2004 Ben Fennema
*
* HISTORY
*
* 10/05/98 dgb Split directory operations into its own file
* Implemented directory reads via do_udf_readdir
* 10/06/98 Made directory operations work!
* 11/17/98 Rewrote directory to support ICBTAG_FLAG_AD_LONG
* 11/25/98 blf Rewrote directory handling (readdir+lookup) to support reading
* across blocks.
* 12/12/98 Split out the lookup code to namei.c. bulk of directory
* code now in directory.c:udf_fileident_read.
*/
#include "udfdecl.h"
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/filelock.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/bio.h>
#include <linux/iversion.h>
#include "udf_i.h"
#include "udf_sb.h"
static int udf_readdir(struct file *file, struct dir_context *ctx)
{
struct inode *dir = file_inode(file);
loff_t nf_pos, emit_pos = 0;
int flen;
unsigned char *fname = NULL;
int ret = 0;
struct super_block *sb = dir->i_sb;
bool pos_valid = false;
struct udf_fileident_iter iter;
if (ctx->pos == 0) {
if (!dir_emit_dot(file, ctx))
return 0;
ctx->pos = 1;
}
nf_pos = (ctx->pos - 1) << 2;
if (nf_pos >= dir->i_size)
goto out;
/*
* Something changed since last readdir (either lseek was called or dir
* changed)? We need to verify the position correctly points at the
* beginning of some dir entry so that the directory parsing code does
* not get confused. Since UDF does not have any reliable way of
* identifying beginning of dir entry (names are under user control),
* we need to scan the directory from the beginning.
*/
if (!inode_eq_iversion(dir, *(u64 *)file->private_data)) {
emit_pos = nf_pos;
nf_pos = 0;
} else {
pos_valid = true;
}
fname = kmalloc(UDF_NAME_LEN, GFP_KERNEL);
if (!fname) {
ret = -ENOMEM;
goto out;
}
for (ret = udf_fiiter_init(&iter, dir, nf_pos);
!ret && iter.pos < dir->i_size;
ret = udf_fiiter_advance(&iter)) {
struct kernel_lb_addr tloc;
udf_pblk_t iblock;
/* Still not at offset where user asked us to read from? */
if (iter.pos < emit_pos)
continue;
/* Update file position only if we got past the current one */
pos_valid = true;
ctx->pos = (iter.pos >> 2) + 1;
if (iter.fi.fileCharacteristics & FID_FILE_CHAR_DELETED) {
if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNDELETE))
continue;
}
if (iter.fi.fileCharacteristics & FID_FILE_CHAR_HIDDEN) {
if (!UDF_QUERY_FLAG(sb, UDF_FLAG_UNHIDE))
continue;
}
if (iter.fi.fileCharacteristics & FID_FILE_CHAR_PARENT) {
if (!dir_emit_dotdot(file, ctx))
goto out_iter;
continue;
}
flen = udf_get_filename(sb, iter.name,
iter.fi.lengthFileIdent, fname, UDF_NAME_LEN);
if (flen < 0)
continue;
tloc = lelb_to_cpu(iter.fi.icb.extLocation);
iblock = udf_get_lb_pblock(sb, &tloc, 0);
if (!dir_emit(ctx, fname, flen, iblock, DT_UNKNOWN))
goto out_iter;
}
if (!ret) {
ctx->pos = (iter.pos >> 2) + 1;
pos_valid = true;
}
out_iter:
udf_fiiter_release(&iter);
out:
if (pos_valid)
*(u64 *)file->private_data = inode_query_iversion(dir);
kfree(fname);
return ret;
}
static int udf_dir_open(struct inode *inode, struct file *file)
{
file->private_data = kzalloc(sizeof(u64), GFP_KERNEL);
if (!file->private_data)
return -ENOMEM;
return 0;
}
static int udf_dir_release(struct inode *inode, struct file *file)
{
kfree(file->private_data);
return 0;
}
static loff_t udf_dir_llseek(struct file *file, loff_t offset, int whence)
{
return generic_llseek_cookie(file, offset, whence,
(u64 *)file->private_data);
}
/* readdir and lookup functions */
const struct file_operations udf_dir_operations = {
.open = udf_dir_open,
.release = udf_dir_release,
.llseek = udf_dir_llseek,
.read = generic_read_dir,
.iterate_shared = udf_readdir,
.unlocked_ioctl = udf_ioctl,
.fsync = udf_fsync,
.setlease = generic_setlease,
};
+543
View File
@@ -0,0 +1,543 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* directory.c
*
* PURPOSE
* Directory related functions
*
*/
#include "udfdecl.h"
#include "udf_i.h"
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/bio.h>
#include <linux/crc-itu-t.h>
#include <linux/iversion.h>
static int udf_verify_fi(struct udf_fileident_iter *iter)
{
unsigned int len;
if (iter->fi.descTag.tagIdent != cpu_to_le16(TAG_IDENT_FID)) {
udf_err(iter->dir->i_sb,
"directory (ino %llu) has entry at pos %llu with incorrect tag %x\n",
iter->dir->i_ino, (unsigned long long)iter->pos,
le16_to_cpu(iter->fi.descTag.tagIdent));
return -EFSCORRUPTED;
}
len = udf_dir_entry_len(&iter->fi);
if (le16_to_cpu(iter->fi.lengthOfImpUse) & 3) {
udf_err(iter->dir->i_sb,
"directory (ino %llu) has entry at pos %llu with unaligned length of impUse field\n",
iter->dir->i_ino, (unsigned long long)iter->pos);
return -EFSCORRUPTED;
}
/*
* This is in fact allowed by the spec due to long impUse field but
* we don't support it. If there is real media with this large impUse
* field, support can be added.
*/
if (len > 1 << iter->dir->i_blkbits) {
udf_err(iter->dir->i_sb,
"directory (ino %llu) has too big (%u) entry at pos %llu\n",
iter->dir->i_ino, len, (unsigned long long)iter->pos);
return -EFSCORRUPTED;
}
if (iter->pos + len > iter->dir->i_size) {
udf_err(iter->dir->i_sb,
"directory (ino %llu) has entry past directory size at pos %llu\n",
iter->dir->i_ino, (unsigned long long)iter->pos);
return -EFSCORRUPTED;
}
if (udf_dir_entry_len(&iter->fi) !=
sizeof(struct tag) + le16_to_cpu(iter->fi.descTag.descCRCLength)) {
udf_err(iter->dir->i_sb,
"directory (ino %llu) has entry where CRC length (%u) does not match entry length (%u)\n",
iter->dir->i_ino,
(unsigned)le16_to_cpu(iter->fi.descTag.descCRCLength),
(unsigned)(udf_dir_entry_len(&iter->fi) -
sizeof(struct tag)));
return -EFSCORRUPTED;
}
return 0;
}
static int udf_copy_fi(struct udf_fileident_iter *iter)
{
struct udf_inode_info *iinfo = UDF_I(iter->dir);
u32 blksize = 1 << iter->dir->i_blkbits;
u32 off, len, nameoff;
int err;
/* Skip copying when we are at EOF */
if (iter->pos >= iter->dir->i_size) {
iter->name = NULL;
return 0;
}
if (iter->dir->i_size < iter->pos + sizeof(struct fileIdentDesc)) {
udf_err(iter->dir->i_sb,
"directory (ino %llu) has entry straddling EOF\n",
iter->dir->i_ino);
return -EFSCORRUPTED;
}
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
memcpy(&iter->fi, iinfo->i_data + iinfo->i_lenEAttr + iter->pos,
sizeof(struct fileIdentDesc));
err = udf_verify_fi(iter);
if (err < 0)
return err;
iter->name = iinfo->i_data + iinfo->i_lenEAttr + iter->pos +
sizeof(struct fileIdentDesc) +
le16_to_cpu(iter->fi.lengthOfImpUse);
return 0;
}
off = iter->pos & (blksize - 1);
len = min_t(u32, sizeof(struct fileIdentDesc), blksize - off);
memcpy(&iter->fi, iter->bh[0]->b_data + off, len);
if (len < sizeof(struct fileIdentDesc))
memcpy((char *)(&iter->fi) + len, iter->bh[1]->b_data,
sizeof(struct fileIdentDesc) - len);
err = udf_verify_fi(iter);
if (err < 0)
return err;
/* Handle directory entry name */
nameoff = off + sizeof(struct fileIdentDesc) +
le16_to_cpu(iter->fi.lengthOfImpUse);
if (off + udf_dir_entry_len(&iter->fi) <= blksize) {
iter->name = iter->bh[0]->b_data + nameoff;
} else if (nameoff >= blksize) {
iter->name = iter->bh[1]->b_data + (nameoff - blksize);
} else {
iter->name = iter->namebuf;
len = blksize - nameoff;
memcpy(iter->name, iter->bh[0]->b_data + nameoff, len);
memcpy(iter->name + len, iter->bh[1]->b_data,
iter->fi.lengthFileIdent - len);
}
return 0;
}
/* Readahead 8k once we are at 8k boundary */
static void udf_readahead_dir(struct udf_fileident_iter *iter)
{
unsigned int ralen = 16 >> (iter->dir->i_blkbits - 9);
struct buffer_head *tmp, *bha[16];
int i, num;
udf_pblk_t blk;
if (iter->loffset & (ralen - 1))
return;
if (iter->loffset + ralen > (iter->elen >> iter->dir->i_blkbits))
ralen = (iter->elen >> iter->dir->i_blkbits) - iter->loffset;
num = 0;
for (i = 0; i < ralen; i++) {
blk = udf_get_lb_pblock(iter->dir->i_sb, &iter->eloc,
iter->loffset + i);
tmp = sb_getblk(iter->dir->i_sb, blk);
if (tmp && !buffer_uptodate(tmp) && !buffer_locked(tmp))
bha[num++] = tmp;
else
brelse(tmp);
}
if (num) {
bh_readahead_batch(num, bha, REQ_RAHEAD);
for (i = 0; i < num; i++)
brelse(bha[i]);
}
}
static struct buffer_head *udf_fiiter_bread_blk(struct udf_fileident_iter *iter)
{
udf_pblk_t blk;
udf_readahead_dir(iter);
blk = udf_get_lb_pblock(iter->dir->i_sb, &iter->eloc, iter->loffset);
return sb_bread(iter->dir->i_sb, blk);
}
/*
* Updates loffset to point to next directory block; eloc, elen & epos are
* updated if we need to traverse to the next extent as well.
*/
static int udf_fiiter_advance_blk(struct udf_fileident_iter *iter)
{
int8_t etype = -1;
int err = 0;
iter->loffset++;
if (iter->loffset < DIV_ROUND_UP(iter->elen, 1<<iter->dir->i_blkbits))
return 0;
iter->loffset = 0;
err = udf_next_aext(iter->dir, &iter->epos, &iter->eloc,
&iter->elen, &etype, 1);
if (err < 0)
return err;
else if (err == 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) {
if (iter->pos == iter->dir->i_size) {
iter->elen = 0;
return 0;
}
udf_err(iter->dir->i_sb,
"extent after position %llu not allocated in directory (ino %llu)\n",
(unsigned long long)iter->pos, iter->dir->i_ino);
return -EFSCORRUPTED;
}
return 0;
}
static int udf_fiiter_load_bhs(struct udf_fileident_iter *iter)
{
int blksize = 1 << iter->dir->i_blkbits;
int off = iter->pos & (blksize - 1);
int err;
struct fileIdentDesc *fi;
/* Is there any further extent we can map from? */
if (!iter->bh[0] && iter->elen) {
iter->bh[0] = udf_fiiter_bread_blk(iter);
if (!iter->bh[0]) {
err = -ENOMEM;
goto out_brelse;
}
if (!buffer_uptodate(iter->bh[0])) {
err = -EIO;
goto out_brelse;
}
}
/* There's no next block so we are done */
if (iter->pos >= iter->dir->i_size)
return 0;
/* Need to fetch next block as well? */
if (off + sizeof(struct fileIdentDesc) > blksize)
goto fetch_next;
fi = (struct fileIdentDesc *)(iter->bh[0]->b_data + off);
/* Need to fetch next block to get name? */
if (off + udf_dir_entry_len(fi) > blksize) {
fetch_next:
err = udf_fiiter_advance_blk(iter);
if (err)
goto out_brelse;
iter->bh[1] = udf_fiiter_bread_blk(iter);
if (!iter->bh[1]) {
err = -ENOMEM;
goto out_brelse;
}
if (!buffer_uptodate(iter->bh[1])) {
err = -EIO;
goto out_brelse;
}
}
return 0;
out_brelse:
brelse(iter->bh[0]);
brelse(iter->bh[1]);
iter->bh[0] = iter->bh[1] = NULL;
return err;
}
int udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir,
loff_t pos)
{
struct udf_inode_info *iinfo = UDF_I(dir);
int err = 0;
int8_t etype;
iter->dir = dir;
iter->bh[0] = iter->bh[1] = NULL;
iter->pos = pos;
iter->elen = 0;
iter->epos.bh = NULL;
iter->name = NULL;
/*
* When directory is verified, we don't expect directory iteration to
* fail and it can be difficult to undo without corrupting filesystem.
* So just do not allow memory allocation failures here.
*/
iter->namebuf = kmalloc(UDF_NAME_LEN_CS0, GFP_KERNEL | __GFP_NOFAIL);
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
err = udf_copy_fi(iter);
goto out;
}
err = inode_bmap(dir, iter->pos >> dir->i_blkbits, &iter->epos,
&iter->eloc, &iter->elen, &iter->loffset, &etype);
if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) {
if (pos == dir->i_size)
return 0;
udf_err(dir->i_sb,
"position %llu not allocated in directory (ino %llu)\n",
(unsigned long long)pos, dir->i_ino);
err = -EFSCORRUPTED;
goto out;
}
err = udf_fiiter_load_bhs(iter);
if (err < 0)
goto out;
err = udf_copy_fi(iter);
out:
if (err < 0)
udf_fiiter_release(iter);
return err;
}
int udf_fiiter_advance(struct udf_fileident_iter *iter)
{
unsigned int oldoff, len;
int blksize = 1 << iter->dir->i_blkbits;
int err;
oldoff = iter->pos & (blksize - 1);
len = udf_dir_entry_len(&iter->fi);
iter->pos += len;
if (UDF_I(iter->dir)->i_alloc_type != ICBTAG_FLAG_AD_IN_ICB) {
if (oldoff + len >= blksize) {
brelse(iter->bh[0]);
iter->bh[0] = NULL;
/* Next block already loaded? */
if (iter->bh[1]) {
iter->bh[0] = iter->bh[1];
iter->bh[1] = NULL;
} else {
err = udf_fiiter_advance_blk(iter);
if (err < 0)
return err;
}
}
err = udf_fiiter_load_bhs(iter);
if (err < 0)
return err;
}
return udf_copy_fi(iter);
}
void udf_fiiter_release(struct udf_fileident_iter *iter)
{
iter->dir = NULL;
brelse(iter->bh[0]);
brelse(iter->bh[1]);
iter->bh[0] = iter->bh[1] = NULL;
kfree(iter->namebuf);
iter->namebuf = NULL;
}
static void udf_copy_to_bufs(void *buf1, int len1, void *buf2, int len2,
int off, void *src, int len)
{
int copy;
if (off >= len1) {
off -= len1;
} else {
copy = min(off + len, len1) - off;
memcpy(buf1 + off, src, copy);
src += copy;
len -= copy;
off = 0;
}
if (len > 0) {
if (WARN_ON_ONCE(off + len > len2 || !buf2))
return;
memcpy(buf2 + off, src, len);
}
}
static uint16_t udf_crc_fi_bufs(void *buf1, int len1, void *buf2, int len2,
int off, int len)
{
int copy;
uint16_t crc = 0;
if (off >= len1) {
off -= len1;
} else {
copy = min(off + len, len1) - off;
crc = crc_itu_t(crc, buf1 + off, copy);
len -= copy;
off = 0;
}
if (len > 0) {
if (WARN_ON_ONCE(off + len > len2 || !buf2))
return 0;
crc = crc_itu_t(crc, buf2 + off, len);
}
return crc;
}
static void udf_copy_fi_to_bufs(char *buf1, int len1, char *buf2, int len2,
int off, struct fileIdentDesc *fi,
uint8_t *impuse, uint8_t *name)
{
uint16_t crc;
int fioff = off;
int crcoff = off + sizeof(struct tag);
unsigned int crclen = udf_dir_entry_len(fi) - sizeof(struct tag);
char zeros[UDF_NAME_PAD] = {};
int endoff = off + udf_dir_entry_len(fi);
udf_copy_to_bufs(buf1, len1, buf2, len2, off, fi,
sizeof(struct fileIdentDesc));
off += sizeof(struct fileIdentDesc);
if (impuse)
udf_copy_to_bufs(buf1, len1, buf2, len2, off, impuse,
le16_to_cpu(fi->lengthOfImpUse));
off += le16_to_cpu(fi->lengthOfImpUse);
if (name) {
udf_copy_to_bufs(buf1, len1, buf2, len2, off, name,
fi->lengthFileIdent);
off += fi->lengthFileIdent;
udf_copy_to_bufs(buf1, len1, buf2, len2, off, zeros,
endoff - off);
}
crc = udf_crc_fi_bufs(buf1, len1, buf2, len2, crcoff, crclen);
fi->descTag.descCRC = cpu_to_le16(crc);
fi->descTag.descCRCLength = cpu_to_le16(crclen);
fi->descTag.tagChecksum = udf_tag_checksum(&fi->descTag);
udf_copy_to_bufs(buf1, len1, buf2, len2, fioff, fi, sizeof(struct tag));
}
void udf_fiiter_write_fi(struct udf_fileident_iter *iter, uint8_t *impuse)
{
struct udf_inode_info *iinfo = UDF_I(iter->dir);
void *buf1, *buf2 = NULL;
int len1, len2 = 0, off;
int blksize = 1 << iter->dir->i_blkbits;
off = iter->pos & (blksize - 1);
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
buf1 = iinfo->i_data + iinfo->i_lenEAttr;
len1 = iter->dir->i_size;
} else {
buf1 = iter->bh[0]->b_data;
len1 = blksize;
if (iter->bh[1]) {
buf2 = iter->bh[1]->b_data;
len2 = blksize;
}
}
udf_copy_fi_to_bufs(buf1, len1, buf2, len2, off, &iter->fi, impuse,
iter->name == iter->namebuf ? iter->name : NULL);
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
mark_inode_dirty(iter->dir);
} else {
mmb_mark_buffer_dirty(iter->bh[0], &iinfo->i_metadata_bhs);
if (iter->bh[1])
mmb_mark_buffer_dirty(iter->bh[1],
&iinfo->i_metadata_bhs);
}
inode_inc_iversion(iter->dir);
}
void udf_fiiter_update_elen(struct udf_fileident_iter *iter, uint32_t new_elen)
{
struct udf_inode_info *iinfo = UDF_I(iter->dir);
int diff = new_elen - iter->elen;
/* Skip update when we already went past the last extent */
if (!iter->elen)
return;
iter->elen = new_elen;
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
iter->epos.offset -= sizeof(struct short_ad);
else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
iter->epos.offset -= sizeof(struct long_ad);
udf_write_aext(iter->dir, &iter->epos, &iter->eloc, iter->elen, 1);
iinfo->i_lenExtents += diff;
mark_inode_dirty(iter->dir);
}
/* Append new block to directory. @iter is expected to point at EOF */
int udf_fiiter_append_blk(struct udf_fileident_iter *iter)
{
struct udf_inode_info *iinfo = UDF_I(iter->dir);
int blksize = 1 << iter->dir->i_blkbits;
struct buffer_head *bh;
sector_t block;
uint32_t old_elen = iter->elen;
int err;
int8_t etype;
if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB))
return -EINVAL;
/* Round up last extent in the file */
udf_fiiter_update_elen(iter, ALIGN(iter->elen, blksize));
/* Allocate new block and refresh mapping information */
block = iinfo->i_lenExtents >> iter->dir->i_blkbits;
bh = udf_bread(iter->dir, block, 1, &err);
if (!bh) {
udf_fiiter_update_elen(iter, old_elen);
return err;
}
err = inode_bmap(iter->dir, block, &iter->epos, &iter->eloc, &iter->elen,
&iter->loffset, &etype);
if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30)) {
udf_err(iter->dir->i_sb,
"block %llu not allocated in directory (ino %llu)\n",
(unsigned long long)block, iter->dir->i_ino);
return -EFSCORRUPTED;
}
if (!(iter->pos & (blksize - 1))) {
brelse(iter->bh[0]);
iter->bh[0] = bh;
} else {
iter->bh[1] = bh;
}
return 0;
}
struct short_ad *udf_get_fileshortad(uint8_t *ptr, int maxoffset, uint32_t *offset,
int inc)
{
struct short_ad *sa;
if ((!ptr) || (!offset)) {
pr_err("%s: invalidparms\n", __func__);
return NULL;
}
if ((*offset + sizeof(struct short_ad)) > maxoffset)
return NULL;
else {
sa = (struct short_ad *)ptr;
if (sa->extLength == 0)
return NULL;
}
if (inc)
*offset += sizeof(struct short_ad);
return sa;
}
struct long_ad *udf_get_filelongad(uint8_t *ptr, int maxoffset, uint32_t *offset, int inc)
{
struct long_ad *la;
if ((!ptr) || (!offset)) {
pr_err("%s: invalidparms\n", __func__);
return NULL;
}
if ((*offset + sizeof(struct long_ad)) > maxoffset)
return NULL;
else {
la = (struct long_ad *)ptr;
if (la->extLength == 0)
return NULL;
}
if (inc)
*offset += sizeof(struct long_ad);
return la;
}
+816
View File
@@ -0,0 +1,816 @@
/*
* ecma_167.h
*
* This file is based on ECMA-167 3rd edition (June 1997)
* https://www.ecma.ch
*
* Copyright (c) 2001-2002 Ben Fennema
* Copyright (c) 2017-2019 Pali Rohár <pali@kernel.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU Public License ("GPL").
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/**
* @file
* ECMA-167r3 defines and structure definitions
*/
#include <linux/types.h>
#ifndef _ECMA_167_H
#define _ECMA_167_H 1
/* Character sets and coding - d-characters (ECMA 167r3 1/7.2) */
typedef uint8_t dchars;
/* Character set specification (ECMA 167r3 1/7.2.1) */
struct charspec {
uint8_t charSetType;
uint8_t charSetInfo[63];
} __packed;
/* Character Set Type (ECMA 167r3 1/7.2.1.1) */
#define CHARSPEC_TYPE_CS0 0x00 /* (1/7.2.2) */
#define CHARSPEC_TYPE_CS1 0x01 /* (1/7.2.3) */
#define CHARSPEC_TYPE_CS2 0x02 /* (1/7.2.4) */
#define CHARSPEC_TYPE_CS3 0x03 /* (1/7.2.5) */
#define CHARSPEC_TYPE_CS4 0x04 /* (1/7.2.6) */
#define CHARSPEC_TYPE_CS5 0x05 /* (1/7.2.7) */
#define CHARSPEC_TYPE_CS6 0x06 /* (1/7.2.8) */
#define CHARSPEC_TYPE_CS7 0x07 /* (1/7.2.9) */
#define CHARSPEC_TYPE_CS8 0x08 /* (1/7.2.10) */
/* Fixed-length character fields - d-string (EMCA 167r3 1/7.2.12) */
typedef uint8_t dstring;
/* Timestamp (ECMA 167r3 1/7.3) */
struct timestamp {
__le16 typeAndTimezone;
__le16 year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint8_t centiseconds;
uint8_t hundredsOfMicroseconds;
uint8_t microseconds;
} __packed;
/* Type and Time Zone (ECMA 167r3 1/7.3.1) */
#define TIMESTAMP_TYPE_MASK 0xF000
#define TIMESTAMP_TYPE_CUT 0x0000
#define TIMESTAMP_TYPE_LOCAL 0x1000
#define TIMESTAMP_TYPE_AGREEMENT 0x2000
#define TIMESTAMP_TIMEZONE_MASK 0x0FFF
/* Entity identifier (ECMA 167r3 1/7.4) */
struct regid {
uint8_t flags;
uint8_t ident[23];
uint8_t identSuffix[8];
} __packed;
/* Flags (ECMA 167r3 1/7.4.1) */
#define ENTITYID_FLAGS_DIRTY 0x01
#define ENTITYID_FLAGS_PROTECTED 0x02
/* Volume Structure Descriptor (ECMA 167r3 2/9.1) */
#define VSD_STD_ID_LEN 5
struct volStructDesc {
uint8_t structType;
uint8_t stdIdent[VSD_STD_ID_LEN];
uint8_t structVersion;
uint8_t structData[2041];
} __packed;
/* Standard Identifier (EMCA 167r2 2/9.1.2) */
#define VSD_STD_ID_NSR02 "NSR02" /* (3/9.1) */
/* Standard Identifier (ECMA 167r3 2/9.1.2) */
#define VSD_STD_ID_BEA01 "BEA01" /* (2/9.2) */
#define VSD_STD_ID_BOOT2 "BOOT2" /* (2/9.4) */
#define VSD_STD_ID_CD001 "CD001" /* (ECMA-119) */
#define VSD_STD_ID_CDW02 "CDW02" /* (ECMA-168) */
#define VSD_STD_ID_NSR03 "NSR03" /* (3/9.1) */
#define VSD_STD_ID_TEA01 "TEA01" /* (2/9.3) */
/* Beginning Extended Area Descriptor (ECMA 167r3 2/9.2) */
struct beginningExtendedAreaDesc {
uint8_t structType;
uint8_t stdIdent[VSD_STD_ID_LEN];
uint8_t structVersion;
uint8_t structData[2041];
} __packed;
/* Terminating Extended Area Descriptor (ECMA 167r3 2/9.3) */
struct terminatingExtendedAreaDesc {
uint8_t structType;
uint8_t stdIdent[VSD_STD_ID_LEN];
uint8_t structVersion;
uint8_t structData[2041];
} __packed;
/* Boot Descriptor (ECMA 167r3 2/9.4) */
struct bootDesc {
uint8_t structType;
uint8_t stdIdent[VSD_STD_ID_LEN];
uint8_t structVersion;
uint8_t reserved1;
struct regid archType;
struct regid bootIdent;
__le32 bootExtLocation;
__le32 bootExtLength;
__le64 loadAddress;
__le64 startAddress;
struct timestamp descCreationDateAndTime;
__le16 flags;
uint8_t reserved2[32];
uint8_t bootUse[1906];
} __packed;
/* Flags (ECMA 167r3 2/9.4.12) */
#define BOOT_FLAGS_ERASE 0x01
/* Extent Descriptor (ECMA 167r3 3/7.1) */
struct extent_ad {
__le32 extLength;
__le32 extLocation;
} __packed;
struct kernel_extent_ad {
uint32_t extLength;
uint32_t extLocation;
};
/* Descriptor Tag (ECMA 167r3 3/7.2) */
struct tag {
__le16 tagIdent;
__le16 descVersion;
uint8_t tagChecksum;
uint8_t reserved;
__le16 tagSerialNum;
__le16 descCRC;
__le16 descCRCLength;
__le32 tagLocation;
} __packed;
/* Tag Identifier (ECMA 167r3 3/7.2.1) */
#define TAG_IDENT_PVD 0x0001
#define TAG_IDENT_AVDP 0x0002
#define TAG_IDENT_VDP 0x0003
#define TAG_IDENT_IUVD 0x0004
#define TAG_IDENT_PD 0x0005
#define TAG_IDENT_LVD 0x0006
#define TAG_IDENT_USD 0x0007
#define TAG_IDENT_TD 0x0008
#define TAG_IDENT_LVID 0x0009
/* NSR Descriptor (ECMA 167r3 3/9.1) */
struct NSRDesc {
uint8_t structType;
uint8_t stdIdent[VSD_STD_ID_LEN];
uint8_t structVersion;
uint8_t reserved;
uint8_t structData[2040];
} __packed;
/* Generic Descriptor */
struct genericDesc {
struct tag descTag;
__le32 volDescSeqNum;
uint8_t reserved[492];
} __packed;
/* Primary Volume Descriptor (ECMA 167r3 3/10.1) */
struct primaryVolDesc {
struct tag descTag;
__le32 volDescSeqNum;
__le32 primaryVolDescNum;
dstring volIdent[32];
__le16 volSeqNum;
__le16 maxVolSeqNum;
__le16 interchangeLvl;
__le16 maxInterchangeLvl;
__le32 charSetList;
__le32 maxCharSetList;
dstring volSetIdent[128];
struct charspec descCharSet;
struct charspec explanatoryCharSet;
struct extent_ad volAbstract;
struct extent_ad volCopyright;
struct regid appIdent;
struct timestamp recordingDateAndTime;
struct regid impIdent;
uint8_t impUse[64];
__le32 predecessorVolDescSeqLocation;
__le16 flags;
uint8_t reserved[22];
} __packed;
/* Flags (ECMA 167r3 3/10.1.21) */
#define PVD_FLAGS_VSID_COMMON 0x0001
/* Anchor Volume Descriptor Pointer (ECMA 167r3 3/10.2) */
struct anchorVolDescPtr {
struct tag descTag;
struct extent_ad mainVolDescSeqExt;
struct extent_ad reserveVolDescSeqExt;
uint8_t reserved[480];
} __packed;
/* Volume Descriptor Pointer (ECMA 167r3 3/10.3) */
struct volDescPtr {
struct tag descTag;
__le32 volDescSeqNum;
struct extent_ad nextVolDescSeqExt;
uint8_t reserved[484];
} __packed;
/* Implementation Use Volume Descriptor (ECMA 167r3 3/10.4) */
struct impUseVolDesc {
struct tag descTag;
__le32 volDescSeqNum;
struct regid impIdent;
uint8_t impUse[460];
} __packed;
/* Partition Descriptor (ECMA 167r3 3/10.5) */
struct partitionDesc {
struct tag descTag;
__le32 volDescSeqNum;
__le16 partitionFlags;
__le16 partitionNumber;
struct regid partitionContents;
uint8_t partitionContentsUse[128];
__le32 accessType;
__le32 partitionStartingLocation;
__le32 partitionLength;
struct regid impIdent;
uint8_t impUse[128];
uint8_t reserved[156];
} __packed;
/* Partition Flags (ECMA 167r3 3/10.5.3) */
#define PD_PARTITION_FLAGS_ALLOC 0x0001
/* Partition Contents (ECMA 167r2 3/10.5.3) */
#define PD_PARTITION_CONTENTS_NSR02 "+NSR02"
/* Partition Contents (ECMA 167r3 3/10.5.5) */
#define PD_PARTITION_CONTENTS_FDC01 "+FDC01"
#define PD_PARTITION_CONTENTS_CD001 "+CD001"
#define PD_PARTITION_CONTENTS_CDW02 "+CDW02"
#define PD_PARTITION_CONTENTS_NSR03 "+NSR03"
/* Access Type (ECMA 167r3 3/10.5.7) */
#define PD_ACCESS_TYPE_NONE 0x00000000
#define PD_ACCESS_TYPE_READ_ONLY 0x00000001
#define PD_ACCESS_TYPE_WRITE_ONCE 0x00000002
#define PD_ACCESS_TYPE_REWRITABLE 0x00000003
#define PD_ACCESS_TYPE_OVERWRITABLE 0x00000004
/* Logical Volume Descriptor (ECMA 167r3 3/10.6) */
struct logicalVolDesc {
struct tag descTag;
__le32 volDescSeqNum;
struct charspec descCharSet;
dstring logicalVolIdent[128];
__le32 logicalBlockSize;
struct regid domainIdent;
uint8_t logicalVolContentsUse[16];
__le32 mapTableLength;
__le32 numPartitionMaps;
struct regid impIdent;
uint8_t impUse[128];
struct extent_ad integritySeqExt;
uint8_t partitionMaps[];
} __packed;
/* Generic Partition Map (ECMA 167r3 3/10.7.1) */
struct genericPartitionMap {
uint8_t partitionMapType;
uint8_t partitionMapLength;
uint8_t partitionMapping[];
} __packed;
/* Partition Map Type (ECMA 167r3 3/10.7.1.1) */
#define GP_PARTITION_MAP_TYPE_UNDEF 0x00
#define GP_PARTITION_MAP_TYPE_1 0x01
#define GP_PARTITION_MAP_TYPE_2 0x02
/* Type 1 Partition Map (ECMA 167r3 3/10.7.2) */
struct genericPartitionMap1 {
uint8_t partitionMapType;
uint8_t partitionMapLength;
__le16 volSeqNum;
__le16 partitionNum;
} __packed;
/* Type 2 Partition Map (ECMA 167r3 3/10.7.3) */
struct genericPartitionMap2 {
uint8_t partitionMapType;
uint8_t partitionMapLength;
uint8_t partitionIdent[62];
} __packed;
/* Unallocated Space Descriptor (ECMA 167r3 3/10.8) */
struct unallocSpaceDesc {
struct tag descTag;
__le32 volDescSeqNum;
__le32 numAllocDescs;
struct extent_ad allocDescs[];
} __packed;
/* Terminating Descriptor (ECMA 167r3 3/10.9) */
struct terminatingDesc {
struct tag descTag;
uint8_t reserved[496];
} __packed;
/* Logical Volume Integrity Descriptor (ECMA 167r3 3/10.10) */
struct logicalVolIntegrityDesc {
struct tag descTag;
struct timestamp recordingDateAndTime;
__le32 integrityType;
struct extent_ad nextIntegrityExt;
uint8_t logicalVolContentsUse[32];
__le32 numOfPartitions;
__le32 lengthOfImpUse;
__le32 freeSpaceTable[];
/* __le32 sizeTable[]; */
/* uint8_t impUse[]; */
} __packed;
/* Integrity Type (ECMA 167r3 3/10.10.3) */
#define LVID_INTEGRITY_TYPE_OPEN 0x00000000
#define LVID_INTEGRITY_TYPE_CLOSE 0x00000001
/* Recorded Address (ECMA 167r3 4/7.1) */
struct lb_addr {
__le32 logicalBlockNum;
__le16 partitionReferenceNum;
} __packed;
/* ... and its in-core analog */
struct kernel_lb_addr {
uint32_t logicalBlockNum;
uint16_t partitionReferenceNum;
};
/* Short Allocation Descriptor (ECMA 167r3 4/14.14.1) */
struct short_ad {
__le32 extLength;
__le32 extPosition;
} __packed;
/* Long Allocation Descriptor (ECMA 167r3 4/14.14.2) */
struct long_ad {
__le32 extLength;
struct lb_addr extLocation;
uint8_t impUse[6];
} __packed;
struct kernel_long_ad {
uint32_t extLength;
struct kernel_lb_addr extLocation;
uint8_t impUse[6];
};
/* Extended Allocation Descriptor (ECMA 167r3 4/14.14.3) */
struct ext_ad {
__le32 extLength;
__le32 recordedLength;
__le32 informationLength;
struct lb_addr extLocation;
} __packed;
struct kernel_ext_ad {
uint32_t extLength;
uint32_t recordedLength;
uint32_t informationLength;
struct kernel_lb_addr extLocation;
};
/* Descriptor Tag (ECMA 167r3 4/7.2 - See 3/7.2) */
/* Tag Identifier (ECMA 167r3 4/7.2.1) */
#define TAG_IDENT_FSD 0x0100
#define TAG_IDENT_FID 0x0101
#define TAG_IDENT_AED 0x0102
#define TAG_IDENT_IE 0x0103
#define TAG_IDENT_TE 0x0104
#define TAG_IDENT_FE 0x0105
#define TAG_IDENT_EAHD 0x0106
#define TAG_IDENT_USE 0x0107
#define TAG_IDENT_SBD 0x0108
#define TAG_IDENT_PIE 0x0109
#define TAG_IDENT_EFE 0x010A
/* File Set Descriptor (ECMA 167r3 4/14.1) */
struct fileSetDesc {
struct tag descTag;
struct timestamp recordingDateAndTime;
__le16 interchangeLvl;
__le16 maxInterchangeLvl;
__le32 charSetList;
__le32 maxCharSetList;
__le32 fileSetNum;
__le32 fileSetDescNum;
struct charspec logicalVolIdentCharSet;
dstring logicalVolIdent[128];
struct charspec fileSetCharSet;
dstring fileSetIdent[32];
dstring copyrightFileIdent[32];
dstring abstractFileIdent[32];
struct long_ad rootDirectoryICB;
struct regid domainIdent;
struct long_ad nextExt;
struct long_ad streamDirectoryICB;
uint8_t reserved[32];
} __packed;
/* Partition Header Descriptor (ECMA 167r3 4/14.3) */
struct partitionHeaderDesc {
struct short_ad unallocSpaceTable;
struct short_ad unallocSpaceBitmap;
struct short_ad partitionIntegrityTable;
struct short_ad freedSpaceTable;
struct short_ad freedSpaceBitmap;
uint8_t reserved[88];
} __packed;
/* File Identifier Descriptor (ECMA 167r3 4/14.4) */
struct fileIdentDesc {
struct tag descTag;
__le16 fileVersionNum;
uint8_t fileCharacteristics;
uint8_t lengthFileIdent;
struct long_ad icb;
__le16 lengthOfImpUse;
/* uint8_t impUse[]; */
/* uint8_t fileIdent[]; */
/* uint8_t padding[]; */
} __packed;
/* File Characteristics (ECMA 167r3 4/14.4.3) */
#define FID_FILE_CHAR_HIDDEN 0x01
#define FID_FILE_CHAR_DIRECTORY 0x02
#define FID_FILE_CHAR_DELETED 0x04
#define FID_FILE_CHAR_PARENT 0x08
#define FID_FILE_CHAR_METADATA 0x10
/* Allocation Ext Descriptor (ECMA 167r3 4/14.5) */
struct allocExtDesc {
struct tag descTag;
__le32 previousAllocExtLocation;
__le32 lengthAllocDescs;
} __packed;
/* ICB Tag (ECMA 167r3 4/14.6) */
struct icbtag {
__le32 priorRecordedNumDirectEntries;
__le16 strategyType;
__le16 strategyParameter;
__le16 numEntries;
uint8_t reserved;
uint8_t fileType;
struct lb_addr parentICBLocation;
__le16 flags;
} __packed;
/* Strategy Type (ECMA 167r3 4/14.6.2) */
#define ICBTAG_STRATEGY_TYPE_UNDEF 0x0000
#define ICBTAG_STRATEGY_TYPE_1 0x0001
#define ICBTAG_STRATEGY_TYPE_2 0x0002
#define ICBTAG_STRATEGY_TYPE_3 0x0003
#define ICBTAG_STRATEGY_TYPE_4 0x0004
/* File Type (ECMA 167r3 4/14.6.6) */
#define ICBTAG_FILE_TYPE_UNDEF 0x00
#define ICBTAG_FILE_TYPE_USE 0x01
#define ICBTAG_FILE_TYPE_PIE 0x02
#define ICBTAG_FILE_TYPE_IE 0x03
#define ICBTAG_FILE_TYPE_DIRECTORY 0x04
#define ICBTAG_FILE_TYPE_REGULAR 0x05
#define ICBTAG_FILE_TYPE_BLOCK 0x06
#define ICBTAG_FILE_TYPE_CHAR 0x07
#define ICBTAG_FILE_TYPE_EA 0x08
#define ICBTAG_FILE_TYPE_FIFO 0x09
#define ICBTAG_FILE_TYPE_SOCKET 0x0A
#define ICBTAG_FILE_TYPE_TE 0x0B
#define ICBTAG_FILE_TYPE_SYMLINK 0x0C
#define ICBTAG_FILE_TYPE_STREAMDIR 0x0D
/* Flags (ECMA 167r3 4/14.6.8) */
#define ICBTAG_FLAG_AD_MASK 0x0007
#define ICBTAG_FLAG_AD_SHORT 0x0000
#define ICBTAG_FLAG_AD_LONG 0x0001
#define ICBTAG_FLAG_AD_EXTENDED 0x0002
#define ICBTAG_FLAG_AD_IN_ICB 0x0003
#define ICBTAG_FLAG_SORTED 0x0008
#define ICBTAG_FLAG_NONRELOCATABLE 0x0010
#define ICBTAG_FLAG_ARCHIVE 0x0020
#define ICBTAG_FLAG_SETUID 0x0040
#define ICBTAG_FLAG_SETGID 0x0080
#define ICBTAG_FLAG_STICKY 0x0100
#define ICBTAG_FLAG_CONTIGUOUS 0x0200
#define ICBTAG_FLAG_SYSTEM 0x0400
#define ICBTAG_FLAG_TRANSFORMED 0x0800
#define ICBTAG_FLAG_MULTIVERSIONS 0x1000
#define ICBTAG_FLAG_STREAM 0x2000
/* Indirect Entry (ECMA 167r3 4/14.7) */
struct indirectEntry {
struct tag descTag;
struct icbtag icbTag;
struct long_ad indirectICB;
} __packed;
/* Terminal Entry (ECMA 167r3 4/14.8) */
struct terminalEntry {
struct tag descTag;
struct icbtag icbTag;
} __packed;
/* File Entry (ECMA 167r3 4/14.9) */
struct fileEntry {
struct tag descTag;
struct icbtag icbTag;
__le32 uid;
__le32 gid;
__le32 permissions;
__le16 fileLinkCount;
uint8_t recordFormat;
uint8_t recordDisplayAttr;
__le32 recordLength;
__le64 informationLength;
__le64 logicalBlocksRecorded;
struct timestamp accessTime;
struct timestamp modificationTime;
struct timestamp attrTime;
__le32 checkpoint;
struct long_ad extendedAttrICB;
struct regid impIdent;
__le64 uniqueID;
__le32 lengthExtendedAttr;
__le32 lengthAllocDescs;
uint8_t extendedAttr[];
/* uint8_t allocDescs[]; */
} __packed;
/* Permissions (ECMA 167r3 4/14.9.5) */
#define FE_PERM_O_EXEC 0x00000001U
#define FE_PERM_O_WRITE 0x00000002U
#define FE_PERM_O_READ 0x00000004U
#define FE_PERM_O_CHATTR 0x00000008U
#define FE_PERM_O_DELETE 0x00000010U
#define FE_PERM_G_EXEC 0x00000020U
#define FE_PERM_G_WRITE 0x00000040U
#define FE_PERM_G_READ 0x00000080U
#define FE_PERM_G_CHATTR 0x00000100U
#define FE_PERM_G_DELETE 0x00000200U
#define FE_PERM_U_EXEC 0x00000400U
#define FE_PERM_U_WRITE 0x00000800U
#define FE_PERM_U_READ 0x00001000U
#define FE_PERM_U_CHATTR 0x00002000U
#define FE_PERM_U_DELETE 0x00004000U
/* Record Format (ECMA 167r3 4/14.9.7) */
#define FE_RECORD_FMT_UNDEF 0x00
#define FE_RECORD_FMT_FIXED_PAD 0x01
#define FE_RECORD_FMT_FIXED 0x02
#define FE_RECORD_FMT_VARIABLE8 0x03
#define FE_RECORD_FMT_VARIABLE16 0x04
#define FE_RECORD_FMT_VARIABLE16_MSB 0x05
#define FE_RECORD_FMT_VARIABLE32 0x06
#define FE_RECORD_FMT_PRINT 0x07
#define FE_RECORD_FMT_LF 0x08
#define FE_RECORD_FMT_CR 0x09
#define FE_RECORD_FMT_CRLF 0x0A
#define FE_RECORD_FMT_LFCR 0x0B
/* Record Display Attributes (ECMA 167r3 4/14.9.8) */
#define FE_RECORD_DISPLAY_ATTR_UNDEF 0x00
#define FE_RECORD_DISPLAY_ATTR_1 0x01
#define FE_RECORD_DISPLAY_ATTR_2 0x02
#define FE_RECORD_DISPLAY_ATTR_3 0x03
/* Extended Attribute Header Descriptor (ECMA 167r3 4/14.10.1) */
struct extendedAttrHeaderDesc {
struct tag descTag;
__le32 impAttrLocation;
__le32 appAttrLocation;
} __packed;
/* Generic Format (ECMA 167r3 4/14.10.2) */
struct genericFormat {
__le32 attrType;
uint8_t attrSubtype;
uint8_t reserved[3];
__le32 attrLength;
uint8_t attrData[];
} __packed;
/* Character Set Information (ECMA 167r3 4/14.10.3) */
struct charSetInfo {
__le32 attrType;
uint8_t attrSubtype;
uint8_t reserved[3];
__le32 attrLength;
__le32 escapeSeqLength;
uint8_t charSetType;
uint8_t escapeSeq[];
} __packed;
/* Alternate Permissions (ECMA 167r3 4/14.10.4) */
struct altPerms {
__le32 attrType;
uint8_t attrSubtype;
uint8_t reserved[3];
__le32 attrLength;
__le16 ownerIdent;
__le16 groupIdent;
__le16 permission;
} __packed;
/* File Times Extended Attribute (ECMA 167r3 4/14.10.5) */
struct fileTimesExtAttr {
__le32 attrType;
uint8_t attrSubtype;
uint8_t reserved[3];
__le32 attrLength;
__le32 dataLength;
__le32 fileTimeExistence;
uint8_t fileTimes;
} __packed;
/* FileTimeExistence (ECMA 167r3 4/14.10.5.6) */
#define FTE_CREATION 0x00000001
#define FTE_DELETION 0x00000004
#define FTE_EFFECTIVE 0x00000008
#define FTE_BACKUP 0x00000002
/* Information Times Extended Attribute (ECMA 167r3 4/14.10.6) */
struct infoTimesExtAttr {
__le32 attrType;
uint8_t attrSubtype;
uint8_t reserved[3];
__le32 attrLength;
__le32 dataLength;
__le32 infoTimeExistence;
uint8_t infoTimes[];
} __packed;
/* Device Specification (ECMA 167r3 4/14.10.7) */
struct deviceSpec {
__le32 attrType;
uint8_t attrSubtype;
uint8_t reserved[3];
__le32 attrLength;
__le32 impUseLength;
__le32 majorDeviceIdent;
__le32 minorDeviceIdent;
uint8_t impUse[];
} __packed;
/* Implementation Use Extended Attr (ECMA 167r3 4/14.10.8) */
struct impUseExtAttr {
__le32 attrType;
uint8_t attrSubtype;
uint8_t reserved[3];
__le32 attrLength;
__le32 impUseLength;
struct regid impIdent;
uint8_t impUse[];
} __packed;
/* Application Use Extended Attribute (ECMA 167r3 4/14.10.9) */
struct appUseExtAttr {
__le32 attrType;
uint8_t attrSubtype;
uint8_t reserved[3];
__le32 attrLength;
__le32 appUseLength;
struct regid appIdent;
uint8_t appUse[];
} __packed;
#define EXTATTR_CHAR_SET 1
#define EXTATTR_ALT_PERMS 3
#define EXTATTR_FILE_TIMES 5
#define EXTATTR_INFO_TIMES 6
#define EXTATTR_DEV_SPEC 12
#define EXTATTR_IMP_USE 2048
#define EXTATTR_APP_USE 65536
#define EXTATTR_SUBTYPE 1
/* Unallocated Space Entry (ECMA 167r3 4/14.11) */
struct unallocSpaceEntry {
struct tag descTag;
struct icbtag icbTag;
__le32 lengthAllocDescs;
uint8_t allocDescs[];
} __packed;
/* Space Bitmap Descriptor (ECMA 167r3 4/14.12) */
struct spaceBitmapDesc {
struct tag descTag;
__le32 numOfBits;
__le32 numOfBytes;
uint8_t bitmap[];
} __packed;
/* Partition Integrity Entry (ECMA 167r3 4/14.13) */
struct partitionIntegrityEntry {
struct tag descTag;
struct icbtag icbTag;
struct timestamp recordingDateAndTime;
uint8_t integrityType;
uint8_t reserved[175];
struct regid impIdent;
uint8_t impUse[256];
} __packed;
/* Short Allocation Descriptor (ECMA 167r3 4/14.14.1) */
/* Extent Length (ECMA 167r3 4/14.14.1.1) */
#define EXT_LENGTH_MASK 0x3FFFFFFF
#define EXT_TYPE_MASK 0xC0000000
#define EXT_RECORDED_ALLOCATED 0x00000000
#define EXT_NOT_RECORDED_ALLOCATED 0x40000000
#define EXT_NOT_RECORDED_NOT_ALLOCATED 0x80000000
#define EXT_NEXT_EXTENT_ALLOCDESCS 0xC0000000
/* Long Allocation Descriptor (ECMA 167r3 4/14.14.2) */
/* Extended Allocation Descriptor (ECMA 167r3 4/14.14.3) */
/* Logical Volume Header Descriptor (ECMA 167r3 4/14.15) */
struct logicalVolHeaderDesc {
__le64 uniqueID;
uint8_t reserved[24];
} __packed;
/* Path Component (ECMA 167r3 4/14.16.1) */
struct pathComponent {
uint8_t componentType;
uint8_t lengthComponentIdent;
__le16 componentFileVersionNum;
dchars componentIdent[];
} __packed;
/* File Entry (ECMA 167r3 4/14.17) */
struct extendedFileEntry {
struct tag descTag;
struct icbtag icbTag;
__le32 uid;
__le32 gid;
__le32 permissions;
__le16 fileLinkCount;
uint8_t recordFormat;
uint8_t recordDisplayAttr;
__le32 recordLength;
__le64 informationLength;
__le64 objectSize;
__le64 logicalBlocksRecorded;
struct timestamp accessTime;
struct timestamp modificationTime;
struct timestamp createTime;
struct timestamp attrTime;
__le32 checkpoint;
__le32 reserved;
struct long_ad extendedAttrICB;
struct long_ad streamDirectoryICB;
struct regid impIdent;
__le64 uniqueID;
__le32 lengthExtendedAttr;
__le32 lengthAllocDescs;
uint8_t extendedAttr[];
/* uint8_t allocDescs[]; */
} __packed;
#endif /* _ECMA_167_H */
+261
View File
@@ -0,0 +1,261 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* file.c
*
* PURPOSE
* File handling routines for the OSTA-UDF(tm) filesystem.
*
* COPYRIGHT
* (C) 1998-1999 Dave Boynton
* (C) 1998-2004 Ben Fennema
* (C) 1999-2000 Stelias Computing Inc
*
* HISTORY
*
* 10/02/98 dgb Attempt to integrate into udf.o
* 10/07/98 Switched to using generic_readpage, etc., like isofs
* And it works!
* 12/06/98 blf Added udf_file_read. uses generic_file_read for all cases but
* ICBTAG_FLAG_AD_IN_ICB.
* 04/06/99 64 bit file handling on 32 bit systems taken from ext2 file.c
* 05/12/99 Preliminary file write support
*/
#include "udfdecl.h"
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include <linux/string.h> /* memset */
#include <linux/capability.h>
#include <linux/errno.h>
#include <linux/filelock.h>
#include <linux/pagemap.h>
#include <linux/uio.h>
#include "udf_i.h"
#include "udf_sb.h"
static vm_fault_t udf_page_mkwrite(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
struct inode *inode = file_inode(vma->vm_file);
struct address_space *mapping = inode->i_mapping;
struct folio *folio = page_folio(vmf->page);
loff_t size;
unsigned int end;
vm_fault_t ret = VM_FAULT_LOCKED;
int err;
sb_start_pagefault(inode->i_sb);
file_update_time(vma->vm_file);
filemap_invalidate_lock_shared(mapping);
folio_lock(folio);
size = i_size_read(inode);
if (folio->mapping != inode->i_mapping || folio_pos(folio) >= size) {
folio_unlock(folio);
ret = VM_FAULT_NOPAGE;
goto out_unlock;
}
/* Space is already allocated for in-ICB file */
if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
goto out_dirty;
if (folio->index == size >> PAGE_SHIFT)
end = size & ~PAGE_MASK;
else
end = PAGE_SIZE;
err = __block_write_begin(folio, 0, end, udf_get_block);
if (err) {
folio_unlock(folio);
ret = vmf_fs_error(err);
goto out_unlock;
}
block_commit_write(folio, 0, end);
out_dirty:
folio_mark_dirty(folio);
folio_wait_stable(folio);
out_unlock:
filemap_invalidate_unlock_shared(mapping);
sb_end_pagefault(inode->i_sb);
return ret;
}
static const struct vm_operations_struct udf_file_vm_ops = {
.fault = filemap_fault,
.map_pages = filemap_map_pages,
.page_mkwrite = udf_page_mkwrite,
};
static ssize_t udf_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
ssize_t retval;
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
struct udf_inode_info *iinfo = UDF_I(inode);
inode_lock(inode);
retval = generic_write_checks(iocb, from);
if (retval <= 0)
goto out;
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB &&
inode->i_sb->s_blocksize < (udf_file_entry_alloc_offset(inode) +
iocb->ki_pos + iov_iter_count(from))) {
filemap_invalidate_lock(inode->i_mapping);
retval = udf_expand_file_adinicb(inode);
filemap_invalidate_unlock(inode->i_mapping);
if (retval)
goto out;
}
retval = __generic_file_write_iter(iocb, from);
out:
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB && retval > 0) {
down_write(&iinfo->i_data_sem);
iinfo->i_lenAlloc = inode->i_size;
up_write(&iinfo->i_data_sem);
}
inode_unlock(inode);
if (retval > 0) {
mark_inode_dirty(inode);
retval = generic_write_sync(iocb, retval);
}
return retval;
}
long udf_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct inode *inode = file_inode(filp);
long old_block, new_block;
int result;
if (file_permission(filp, MAY_READ) != 0) {
udf_debug("no permission to access inode %llu\n", inode->i_ino);
return -EPERM;
}
if (!arg && ((cmd == UDF_GETVOLIDENT) || (cmd == UDF_GETEASIZE) ||
(cmd == UDF_RELOCATE_BLOCKS) || (cmd == UDF_GETEABLOCK))) {
udf_debug("invalid argument to udf_ioctl\n");
return -EINVAL;
}
switch (cmd) {
case UDF_GETVOLIDENT:
if (copy_to_user((char __user *)arg,
UDF_SB(inode->i_sb)->s_volume_ident, 32))
return -EFAULT;
return 0;
case UDF_RELOCATE_BLOCKS:
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
if (get_user(old_block, (long __user *)arg))
return -EFAULT;
result = udf_relocate_blocks(inode->i_sb,
old_block, &new_block);
if (result == 0)
result = put_user(new_block, (long __user *)arg);
return result;
case UDF_GETEASIZE:
return put_user(UDF_I(inode)->i_lenEAttr, (int __user *)arg);
case UDF_GETEABLOCK:
return copy_to_user((char __user *)arg,
UDF_I(inode)->i_data,
UDF_I(inode)->i_lenEAttr) ? -EFAULT : 0;
default:
return -ENOIOCTLCMD;
}
return 0;
}
static int udf_release_file(struct inode *inode, struct file *filp)
{
if (filp->f_mode & FMODE_WRITE &&
atomic_read(&inode->i_writecount) == 1) {
/*
* Grab i_mutex to avoid races with writes changing i_size
* while we are running.
*/
inode_lock(inode);
down_write(&UDF_I(inode)->i_data_sem);
udf_discard_prealloc(inode);
udf_truncate_tail_extent(inode);
up_write(&UDF_I(inode)->i_data_sem);
inode_unlock(inode);
}
return 0;
}
static int udf_file_mmap(struct file *file, struct vm_area_struct *vma)
{
file_accessed(file);
vma->vm_ops = &udf_file_vm_ops;
return 0;
}
int udf_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
return mmb_fsync(file,
&UDF_I(file->f_mapping->host)->i_metadata_bhs,
start, end, datasync);
}
const struct file_operations udf_file_operations = {
.read_iter = generic_file_read_iter,
.unlocked_ioctl = udf_ioctl,
.open = generic_file_open,
.mmap = udf_file_mmap,
.write_iter = udf_file_write_iter,
.release = udf_release_file,
.fsync = udf_fsync,
.splice_read = filemap_splice_read,
.splice_write = iter_file_splice_write,
.llseek = generic_file_llseek,
.setlease = generic_setlease,
};
static int udf_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
struct iattr *attr)
{
struct inode *inode = d_inode(dentry);
struct super_block *sb = inode->i_sb;
int error;
error = setattr_prepare(&nop_mnt_idmap, dentry, attr);
if (error)
return error;
if ((attr->ia_valid & ATTR_UID) &&
UDF_QUERY_FLAG(sb, UDF_FLAG_UID_SET) &&
!uid_eq(attr->ia_uid, UDF_SB(sb)->s_uid))
return -EPERM;
if ((attr->ia_valid & ATTR_GID) &&
UDF_QUERY_FLAG(sb, UDF_FLAG_GID_SET) &&
!gid_eq(attr->ia_gid, UDF_SB(sb)->s_gid))
return -EPERM;
if ((attr->ia_valid & ATTR_SIZE) &&
attr->ia_size != i_size_read(inode)) {
filemap_invalidate_lock(inode->i_mapping);
error = udf_setsize(inode, attr->ia_size);
filemap_invalidate_unlock(inode->i_mapping);
if (error)
return error;
}
if (attr->ia_valid & ATTR_MODE)
udf_update_extra_perms(inode, attr->ia_mode);
setattr_copy(&nop_mnt_idmap, inode, attr);
mark_inode_dirty(inode);
return 0;
}
const struct inode_operations udf_file_inode_operations = {
.setattr = udf_setattr,
};
+113
View File
@@ -0,0 +1,113 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* ialloc.c
*
* PURPOSE
* Inode allocation handling routines for the OSTA-UDF(tm) filesystem.
*
* COPYRIGHT
* (C) 1998-2001 Ben Fennema
*
* HISTORY
*
* 02/24/99 blf Created.
*
*/
#include "udfdecl.h"
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include "udf_i.h"
#include "udf_sb.h"
void udf_free_inode(struct inode *inode)
{
udf_free_blocks(inode->i_sb, NULL, &UDF_I(inode)->i_location, 0, 1);
}
struct inode *udf_new_inode(struct inode *dir, umode_t mode)
{
struct super_block *sb = dir->i_sb;
struct udf_sb_info *sbi = UDF_SB(sb);
struct inode *inode;
udf_pblk_t block;
uint32_t start = UDF_I(dir)->i_location.logicalBlockNum;
struct udf_inode_info *iinfo;
struct udf_inode_info *dinfo = UDF_I(dir);
int err;
inode = new_inode(sb);
if (!inode)
return ERR_PTR(-ENOMEM);
iinfo = UDF_I(inode);
if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_EXTENDED_FE)) {
iinfo->i_efe = 1;
if (UDF_VERS_USE_EXTENDED_FE > sbi->s_udfrev)
sbi->s_udfrev = UDF_VERS_USE_EXTENDED_FE;
iinfo->i_data = kzalloc(inode->i_sb->s_blocksize -
sizeof(struct extendedFileEntry),
GFP_KERNEL);
} else {
iinfo->i_efe = 0;
iinfo->i_data = kzalloc(inode->i_sb->s_blocksize -
sizeof(struct fileEntry),
GFP_KERNEL);
}
if (!iinfo->i_data) {
make_bad_inode(inode);
iput(inode);
return ERR_PTR(-ENOMEM);
}
err = -ENOSPC;
block = udf_new_block(dir->i_sb, NULL,
dinfo->i_location.partitionReferenceNum,
start, &err);
if (err) {
make_bad_inode(inode);
iput(inode);
return ERR_PTR(err);
}
iinfo->i_unique = lvid_get_unique_id(sb);
inode->i_generation = iinfo->i_unique;
inode_init_owner(&nop_mnt_idmap, inode, dir, mode);
if (UDF_QUERY_FLAG(sb, UDF_FLAG_UID_SET))
inode->i_uid = sbi->s_uid;
if (UDF_QUERY_FLAG(sb, UDF_FLAG_GID_SET))
inode->i_gid = sbi->s_gid;
iinfo->i_location.logicalBlockNum = block;
iinfo->i_location.partitionReferenceNum =
dinfo->i_location.partitionReferenceNum;
inode->i_ino = udf_get_lb_pblock(sb, &iinfo->i_location, 0);
inode->i_blocks = 0;
iinfo->i_lenEAttr = 0;
iinfo->i_lenAlloc = 0;
iinfo->i_use = 0;
iinfo->i_checkpoint = 1;
iinfo->i_extraPerms = FE_PERM_U_CHATTR;
udf_update_extra_perms(inode, mode);
if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_AD_IN_ICB))
iinfo->i_alloc_type = ICBTAG_FLAG_AD_IN_ICB;
else if (UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_USE_SHORT_AD))
iinfo->i_alloc_type = ICBTAG_FLAG_AD_SHORT;
else
iinfo->i_alloc_type = ICBTAG_FLAG_AD_LONG;
simple_inode_init_ts(inode);
iinfo->i_crtime = inode_get_mtime(inode);
if (unlikely(insert_inode_locked(inode) < 0)) {
make_bad_inode(inode);
iput(inode);
return ERR_PTR(-EIO);
}
mark_inode_dirty(inode);
return inode;
}
File diff suppressed because it is too large Load Diff
+62
View File
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* lowlevel.c
*
* PURPOSE
* Low Level Device Routines for the UDF filesystem
*
* COPYRIGHT
* (C) 1999-2001 Ben Fennema
*
* HISTORY
*
* 03/26/99 blf Created.
*/
#include "udfdecl.h"
#include <linux/blkdev.h>
#include <linux/cdrom.h>
#include <linux/uaccess.h>
#include "udf_sb.h"
unsigned int udf_get_last_session(struct super_block *sb)
{
struct cdrom_device_info *cdi = disk_to_cdi(sb->s_bdev->bd_disk);
struct cdrom_multisession ms_info;
if (!cdi) {
udf_debug("CDROMMULTISESSION not supported.\n");
return 0;
}
ms_info.addr_format = CDROM_LBA;
if (cdrom_multisession(cdi, &ms_info) == 0) {
udf_debug("XA disk: %s, vol_desc_start=%d\n",
ms_info.xa_flag ? "yes" : "no", ms_info.addr.lba);
if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */
return ms_info.addr.lba;
}
return 0;
}
udf_pblk_t udf_get_last_block(struct super_block *sb)
{
struct cdrom_device_info *cdi = disk_to_cdi(sb->s_bdev->bd_disk);
unsigned long lblock = 0;
/*
* The cdrom layer call failed or returned obviously bogus value?
* Try using the device size...
*/
if (!cdi || cdrom_get_last_written(cdi, &lblock) || lblock == 0) {
if (sb_bdev_nr_blocks(sb) > ~(udf_pblk_t)0)
return 0;
lblock = sb_bdev_nr_blocks(sb);
}
if (lblock)
return lblock - 1;
return 0;
}
+289
View File
@@ -0,0 +1,289 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* misc.c
*
* PURPOSE
* Miscellaneous routines for the OSTA-UDF(tm) filesystem.
*
* COPYRIGHT
* (C) 1998 Dave Boynton
* (C) 1998-2004 Ben Fennema
* (C) 1999-2000 Stelias Computing Inc
*
* HISTORY
*
* 04/19/99 blf partial support for reading/writing specific EA's
*/
#include "udfdecl.h"
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/crc-itu-t.h>
#include "udf_i.h"
#include "udf_sb.h"
struct genericFormat *udf_add_extendedattr(struct inode *inode, uint32_t size,
uint32_t type, uint8_t loc)
{
uint8_t *ea = NULL, *ad = NULL;
int offset;
uint16_t crclen;
struct udf_inode_info *iinfo = UDF_I(inode);
ea = iinfo->i_data;
if (iinfo->i_lenEAttr) {
ad = iinfo->i_data + iinfo->i_lenEAttr;
} else {
ad = ea;
size += sizeof(struct extendedAttrHeaderDesc);
}
offset = inode->i_sb->s_blocksize - udf_file_entry_alloc_offset(inode) -
iinfo->i_lenAlloc;
/* TODO - Check for FreeEASpace */
if (loc & 0x01 && offset >= size) {
struct extendedAttrHeaderDesc *eahd;
eahd = (struct extendedAttrHeaderDesc *)ea;
if (iinfo->i_lenAlloc)
memmove(&ad[size], ad, iinfo->i_lenAlloc);
if (iinfo->i_lenEAttr) {
/* check checksum/crc */
if (eahd->descTag.tagIdent !=
cpu_to_le16(TAG_IDENT_EAHD) ||
le32_to_cpu(eahd->descTag.tagLocation) !=
iinfo->i_location.logicalBlockNum)
return NULL;
} else {
struct udf_sb_info *sbi = UDF_SB(inode->i_sb);
size -= sizeof(struct extendedAttrHeaderDesc);
iinfo->i_lenEAttr +=
sizeof(struct extendedAttrHeaderDesc);
eahd->descTag.tagIdent = cpu_to_le16(TAG_IDENT_EAHD);
if (sbi->s_udfrev >= 0x0200)
eahd->descTag.descVersion = cpu_to_le16(3);
else
eahd->descTag.descVersion = cpu_to_le16(2);
eahd->descTag.tagSerialNum =
cpu_to_le16(sbi->s_serial_number);
eahd->descTag.tagLocation = cpu_to_le32(
iinfo->i_location.logicalBlockNum);
eahd->impAttrLocation = cpu_to_le32(0xFFFFFFFF);
eahd->appAttrLocation = cpu_to_le32(0xFFFFFFFF);
}
offset = iinfo->i_lenEAttr;
if (type < 2048) {
if (le32_to_cpu(eahd->appAttrLocation) <
iinfo->i_lenEAttr) {
uint32_t aal =
le32_to_cpu(eahd->appAttrLocation);
memmove(&ea[offset - aal + size],
&ea[aal], offset - aal);
offset -= aal;
eahd->appAttrLocation =
cpu_to_le32(aal + size);
}
if (le32_to_cpu(eahd->impAttrLocation) <
iinfo->i_lenEAttr) {
uint32_t ial =
le32_to_cpu(eahd->impAttrLocation);
memmove(&ea[offset - ial + size],
&ea[ial], offset - ial);
offset -= ial;
eahd->impAttrLocation =
cpu_to_le32(ial + size);
}
} else if (type < 65536) {
if (le32_to_cpu(eahd->appAttrLocation) <
iinfo->i_lenEAttr) {
uint32_t aal =
le32_to_cpu(eahd->appAttrLocation);
memmove(&ea[offset - aal + size],
&ea[aal], offset - aal);
offset -= aal;
eahd->appAttrLocation =
cpu_to_le32(aal + size);
}
}
/* rewrite CRC + checksum of eahd */
crclen = sizeof(struct extendedAttrHeaderDesc) - sizeof(struct tag);
eahd->descTag.descCRCLength = cpu_to_le16(crclen);
eahd->descTag.descCRC = cpu_to_le16(crc_itu_t(0, (char *)eahd +
sizeof(struct tag), crclen));
eahd->descTag.tagChecksum = udf_tag_checksum(&eahd->descTag);
iinfo->i_lenEAttr += size;
return (struct genericFormat *)&ea[offset];
}
return NULL;
}
struct genericFormat *udf_get_extendedattr(struct inode *inode, uint32_t type,
uint8_t subtype)
{
struct genericFormat *gaf;
uint8_t *ea = NULL;
uint32_t offset;
struct udf_inode_info *iinfo = UDF_I(inode);
ea = iinfo->i_data;
if (iinfo->i_lenEAttr) {
struct extendedAttrHeaderDesc *eahd;
eahd = (struct extendedAttrHeaderDesc *)ea;
/* check checksum/crc */
if (eahd->descTag.tagIdent !=
cpu_to_le16(TAG_IDENT_EAHD) ||
le32_to_cpu(eahd->descTag.tagLocation) !=
iinfo->i_location.logicalBlockNum)
return NULL;
if (type < 2048)
offset = sizeof(struct extendedAttrHeaderDesc);
else if (type < 65536)
offset = le32_to_cpu(eahd->impAttrLocation);
else
offset = le32_to_cpu(eahd->appAttrLocation);
while (offset + sizeof(*gaf) < iinfo->i_lenEAttr) {
uint32_t attrLength;
gaf = (struct genericFormat *)&ea[offset];
attrLength = le32_to_cpu(gaf->attrLength);
/* Detect undersized elements and buffer overflows */
if ((attrLength < sizeof(*gaf)) ||
(attrLength > (iinfo->i_lenEAttr - offset)))
break;
if (le32_to_cpu(gaf->attrType) == type &&
gaf->attrSubtype == subtype)
return gaf;
else
offset += attrLength;
}
}
return NULL;
}
/*
* udf_read_tagged
*
* PURPOSE
* Read the first block of a tagged descriptor.
*
* HISTORY
* July 1, 1997 - Andrew E. Mileski
* Written, tested, and released.
*/
struct buffer_head *udf_read_tagged(struct super_block *sb, uint32_t block,
uint32_t location, uint16_t *ident)
{
struct tag *tag_p;
struct buffer_head *bh = NULL;
u8 checksum;
/* Read the block */
if (block == 0xFFFFFFFF)
return NULL;
bh = sb_bread(sb, block);
if (!bh) {
udf_err(sb, "read failed, block=%u, location=%u\n",
block, location);
return NULL;
}
tag_p = (struct tag *)(bh->b_data);
*ident = le16_to_cpu(tag_p->tagIdent);
if (location != le32_to_cpu(tag_p->tagLocation)) {
udf_debug("location mismatch block %u, tag %u != %u\n",
block, le32_to_cpu(tag_p->tagLocation), location);
goto error_out;
}
/* Verify the tag checksum */
checksum = udf_tag_checksum(tag_p);
if (checksum != tag_p->tagChecksum) {
udf_err(sb, "tag checksum failed, block %u: 0x%02x != 0x%02x\n",
block, checksum, tag_p->tagChecksum);
goto error_out;
}
/* Verify the tag version */
if (tag_p->descVersion != cpu_to_le16(0x0002U) &&
tag_p->descVersion != cpu_to_le16(0x0003U)) {
udf_err(sb, "tag version 0x%04x != 0x0002 || 0x0003, block %u\n",
le16_to_cpu(tag_p->descVersion), block);
goto error_out;
}
/* Verify the descriptor CRC */
if (le16_to_cpu(tag_p->descCRCLength) + sizeof(struct tag) > sb->s_blocksize) {
udf_err(sb, "block %u: CRC length %u exceeds block size\n",
block, le16_to_cpu(tag_p->descCRCLength));
goto error_out;
}
if (le16_to_cpu(tag_p->descCRC) == crc_itu_t(0,
bh->b_data + sizeof(struct tag),
le16_to_cpu(tag_p->descCRCLength)))
return bh;
udf_debug("Crc failure block %u: crc = %u, crclen = %u\n", block,
le16_to_cpu(tag_p->descCRC),
le16_to_cpu(tag_p->descCRCLength));
error_out:
brelse(bh);
return NULL;
}
struct buffer_head *udf_read_ptagged(struct super_block *sb,
struct kernel_lb_addr *loc,
uint32_t offset, uint16_t *ident)
{
return udf_read_tagged(sb, udf_get_lb_pblock(sb, loc, offset),
loc->logicalBlockNum + offset, ident);
}
void udf_update_tag(char *data, int length)
{
struct tag *tptr = (struct tag *)data;
length -= sizeof(struct tag);
tptr->descCRCLength = cpu_to_le16(length);
tptr->descCRC = cpu_to_le16(crc_itu_t(0, data + sizeof(struct tag), length));
tptr->tagChecksum = udf_tag_checksum(tptr);
}
void udf_new_tag(char *data, uint16_t ident, uint16_t version, uint16_t snum,
uint32_t loc, int length)
{
struct tag *tptr = (struct tag *)data;
tptr->tagIdent = cpu_to_le16(ident);
tptr->descVersion = cpu_to_le16(version);
tptr->tagSerialNum = cpu_to_le16(snum);
tptr->tagLocation = cpu_to_le32(loc);
udf_update_tag(data, length);
}
u8 udf_tag_checksum(const struct tag *t)
{
u8 *data = (u8 *)t;
u8 checksum = 0;
int i;
for (i = 0; i < sizeof(struct tag); ++i)
if (i != 4) /* position of checksum */
checksum += data[i];
return checksum;
}
File diff suppressed because it is too large Load Diff
+305
View File
@@ -0,0 +1,305 @@
/*
* osta_udf.h
*
* This file is based on OSTA UDF(tm) 2.60 (March 1, 2005)
* http://www.osta.org
*
* Copyright (c) 2001-2004 Ben Fennema
* Copyright (c) 2017-2019 Pali Rohár <pali@kernel.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* Alternatively, this software may be distributed under the terms of the
* GNU Public License ("GPL").
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/**
* @file
* OSTA-UDF defines and structure definitions
*/
#include "ecma_167.h"
#ifndef _OSTA_UDF_H
#define _OSTA_UDF_H 1
/* OSTA CS0 Charspec (UDF 2.60 2.1.2) */
#define UDF_CHAR_SET_TYPE 0
#define UDF_CHAR_SET_INFO "OSTA Compressed Unicode"
/* Entity Identifier (UDF 2.60 2.1.5) */
/* Identifiers (UDF 2.60 2.1.5.2) */
/* Implementation Use Extended Attribute (UDF 2.60 3.3.4.5) */
/* Virtual Allocation Table (UDF 1.50 2.2.10) */
/* Logical Volume Extended Information (UDF 1.50 Errata, DCN 5003, 3.3.4.5.1.3) */
/* OS2EA (UDF 1.50 3.3.4.5.3.1) */
/* MacUniqueIDTable (UDF 1.50 3.3.4.5.4.3) */
/* MacResourceFork (UDF 1.50 3.3.4.5.4.4) */
#define UDF_ID_DEVELOPER "*Linux UDFFS"
#define UDF_ID_COMPLIANT "*OSTA UDF Compliant"
#define UDF_ID_LV_INFO "*UDF LV Info"
#define UDF_ID_FREE_EA "*UDF FreeEASpace"
#define UDF_ID_FREE_APP_EA "*UDF FreeAppEASpace"
#define UDF_ID_DVD_CGMS "*UDF DVD CGMS Info"
#define UDF_ID_VAT_LVEXTENSION "*UDF VAT LVExtension"
#define UDF_ID_OS2_EA "*UDF OS/2 EA"
#define UDF_ID_OS2_EA_LENGTH "*UDF OS/2 EALength"
#define UDF_ID_MAC_VOLUME "*UDF Mac VolumeInfo"
#define UDF_ID_MAC_FINDER "*UDF Mac FinderInfo"
#define UDF_ID_MAC_UNIQUE "*UDF Mac UniqueIDTable"
#define UDF_ID_MAC_RESOURCE "*UDF Mac ResourceFork"
#define UDF_ID_OS400_DIRINFO "*UDF OS/400 DirInfo"
#define UDF_ID_VIRTUAL "*UDF Virtual Partition"
#define UDF_ID_SPARABLE "*UDF Sparable Partition"
#define UDF_ID_ALLOC "*UDF Virtual Alloc Tbl"
#define UDF_ID_SPARING "*UDF Sparing Table"
#define UDF_ID_METADATA "*UDF Metadata Partition"
/* Identifier Suffix (UDF 2.60 2.1.5.3) */
#define DOMAIN_FLAGS_HARD_WRITE_PROTECT 0x01
#define DOMAIN_FLAGS_SOFT_WRITE_PROTECT 0x02
struct domainIdentSuffix {
__le16 UDFRevision;
uint8_t domainFlags;
uint8_t reserved[5];
} __packed;
struct UDFIdentSuffix {
__le16 UDFRevision;
uint8_t OSClass;
uint8_t OSIdentifier;
uint8_t reserved[4];
} __packed;
struct impIdentSuffix {
uint8_t OSClass;
uint8_t OSIdentifier;
uint8_t impUse[6];
} __packed;
struct appIdentSuffix {
uint8_t impUse[8];
} __packed;
/* Logical Volume Integrity Descriptor (UDF 2.60 2.2.6) */
/* Implementation Use (UDF 2.60 2.2.6.4) */
struct logicalVolIntegrityDescImpUse {
struct regid impIdent;
__le32 numFiles;
__le32 numDirs;
__le16 minUDFReadRev;
__le16 minUDFWriteRev;
__le16 maxUDFWriteRev;
uint8_t impUse[];
} __packed;
/* Implementation Use Volume Descriptor (UDF 2.60 2.2.7) */
/* Implementation Use (UDF 2.60 2.2.7.2) */
struct impUseVolDescImpUse {
struct charspec LVICharset;
dstring logicalVolIdent[128];
dstring LVInfo1[36];
dstring LVInfo2[36];
dstring LVInfo3[36];
struct regid impIdent;
uint8_t impUse[128];
} __packed;
struct udfPartitionMap2 {
uint8_t partitionMapType;
uint8_t partitionMapLength;
uint8_t reserved1[2];
struct regid partIdent;
__le16 volSeqNum;
__le16 partitionNum;
} __packed;
/* Virtual Partition Map (UDF 2.60 2.2.8) */
struct virtualPartitionMap {
uint8_t partitionMapType;
uint8_t partitionMapLength;
uint8_t reserved1[2];
struct regid partIdent;
__le16 volSeqNum;
__le16 partitionNum;
uint8_t reserved2[24];
} __packed;
/* Sparable Partition Map (UDF 2.60 2.2.9) */
struct sparablePartitionMap {
uint8_t partitionMapType;
uint8_t partitionMapLength;
uint8_t reserved1[2];
struct regid partIdent;
__le16 volSeqNum;
__le16 partitionNum;
__le16 packetLength;
uint8_t numSparingTables;
uint8_t reserved2[1];
__le32 sizeSparingTable;
__le32 locSparingTable[4];
} __packed;
/* Metadata Partition Map (UDF 2.60 2.2.10) */
struct metadataPartitionMap {
uint8_t partitionMapType;
uint8_t partitionMapLength;
uint8_t reserved1[2];
struct regid partIdent;
__le16 volSeqNum;
__le16 partitionNum;
__le32 metadataFileLoc;
__le32 metadataMirrorFileLoc;
__le32 metadataBitmapFileLoc;
__le32 allocUnitSize;
__le16 alignUnitSize;
uint8_t flags;
uint8_t reserved2[5];
} __packed;
/* Virtual Allocation Table (UDF 2.60 2.2.11) */
struct virtualAllocationTable20 {
__le16 lengthHeader;
__le16 lengthImpUse;
dstring logicalVolIdent[128];
__le32 previousVATICBLoc;
__le32 numFiles;
__le32 numDirs;
__le16 minUDFReadRev;
__le16 minUDFWriteRev;
__le16 maxUDFWriteRev;
__le16 reserved;
uint8_t impUse[];
/* __le32 vatEntry[]; */
} __packed;
#define ICBTAG_FILE_TYPE_VAT20 0xF8U
/* Sparing Table (UDF 2.60 2.2.12) */
struct sparingEntry {
__le32 origLocation;
__le32 mappedLocation;
} __packed;
struct sparingTable {
struct tag descTag;
struct regid sparingIdent;
__le16 reallocationTableLen;
__le16 reserved;
__le32 sequenceNum;
struct sparingEntry mapEntry[];
} __packed;
/* Metadata File (and Metadata Mirror File) (UDF 2.60 2.2.13.1) */
#define ICBTAG_FILE_TYPE_MAIN 0xFA
#define ICBTAG_FILE_TYPE_MIRROR 0xFB
#define ICBTAG_FILE_TYPE_BITMAP 0xFC
/* struct long_ad ICB - ADImpUse (UDF 2.60 2.2.4.3) */
struct allocDescImpUse {
__le16 flags;
uint8_t impUse[4];
} __packed;
#define AD_IU_EXT_ERASED 0x0001
/* Real-Time Files (UDF 2.60 6.11) */
#define ICBTAG_FILE_TYPE_REALTIME 0xF9U
/* Implementation Use Extended Attribute (UDF 2.60 3.3.4.5) */
/* FreeEASpace (UDF 2.60 3.3.4.5.1.1) */
struct freeEaSpace {
__le16 headerChecksum;
uint8_t freeEASpace[];
} __packed;
/* DVD Copyright Management Information (UDF 2.60 3.3.4.5.1.2) */
struct DVDCopyrightImpUse {
__le16 headerChecksum;
uint8_t CGMSInfo;
uint8_t dataType;
uint8_t protectionSystemInfo[4];
} __packed;
/* Logical Volume Extended Information (UDF 1.50 Errata, DCN 5003, 3.3.4.5.1.3) */
struct LVExtensionEA {
__le16 headerChecksum;
__le64 verificationID;
__le32 numFiles;
__le32 numDirs;
dstring logicalVolIdent[128];
} __packed;
/* Application Use Extended Attribute (UDF 2.60 3.3.4.6) */
/* FreeAppEASpace (UDF 2.60 3.3.4.6.1) */
struct freeAppEASpace {
__le16 headerChecksum;
uint8_t freeEASpace[];
} __packed;
/* UDF Defined System Stream (UDF 2.60 3.3.7) */
#define UDF_ID_UNIQUE_ID "*UDF Unique ID Mapping Data"
#define UDF_ID_NON_ALLOC "*UDF Non-Allocatable Space"
#define UDF_ID_POWER_CAL "*UDF Power Cal Table"
#define UDF_ID_BACKUP "*UDF Backup"
/* UDF Defined Non-System Streams (UDF 2.60 3.3.8) */
#define UDF_ID_MAC_RESOURCE_FORK_STREAM "*UDF Macintosh Resource Fork"
/* #define UDF_ID_OS2_EA "*UDF OS/2 EA" */
#define UDF_ID_NT_ACL "*UDF NT ACL"
#define UDF_ID_UNIX_ACL "*UDF UNIX ACL"
/* Operating System Identifiers (UDF 2.60 6.3) */
#define UDF_OS_CLASS_UNDEF 0x00U
#define UDF_OS_CLASS_DOS 0x01U
#define UDF_OS_CLASS_OS2 0x02U
#define UDF_OS_CLASS_MAC 0x03U
#define UDF_OS_CLASS_UNIX 0x04U
#define UDF_OS_CLASS_WIN9X 0x05U
#define UDF_OS_CLASS_WINNT 0x06U
#define UDF_OS_CLASS_OS400 0x07U
#define UDF_OS_CLASS_BEOS 0x08U
#define UDF_OS_CLASS_WINCE 0x09U
#define UDF_OS_ID_UNDEF 0x00U
#define UDF_OS_ID_DOS 0x00U
#define UDF_OS_ID_OS2 0x00U
#define UDF_OS_ID_MAC 0x00U
#define UDF_OS_ID_MAX_OSX 0x01U
#define UDF_OS_ID_UNIX 0x00U
#define UDF_OS_ID_AIX 0x01U
#define UDF_OS_ID_SOLARIS 0x02U
#define UDF_OS_ID_HPUX 0x03U
#define UDF_OS_ID_IRIX 0x04U
#define UDF_OS_ID_LINUX 0x05U
#define UDF_OS_ID_MKLINUX 0x06U
#define UDF_OS_ID_FREEBSD 0x07U
#define UDF_OS_ID_NETBSD 0x08U
#define UDF_OS_ID_WIN9X 0x00U
#define UDF_OS_ID_WINNT 0x00U
#define UDF_OS_ID_OS400 0x00U
#define UDF_OS_ID_BEOS 0x00U
#define UDF_OS_ID_WINCE 0x00U
#endif /* _OSTA_UDF_H */
+340
View File
@@ -0,0 +1,340 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* partition.c
*
* PURPOSE
* Partition handling routines for the OSTA-UDF(tm) filesystem.
*
* COPYRIGHT
* (C) 1998-2001 Ben Fennema
*
* HISTORY
*
* 12/06/98 blf Created file.
*
*/
#include "udfdecl.h"
#include "udf_sb.h"
#include "udf_i.h"
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/mutex.h>
uint32_t udf_get_pblock(struct super_block *sb, uint32_t block,
uint16_t partition, uint32_t offset)
{
struct udf_sb_info *sbi = UDF_SB(sb);
struct udf_part_map *map;
if (partition >= sbi->s_partitions) {
udf_debug("block=%u, partition=%u, offset=%u: invalid partition\n",
block, partition, offset);
return 0xFFFFFFFF;
}
map = &sbi->s_partmaps[partition];
if (map->s_partition_func)
return map->s_partition_func(sb, block, partition, offset);
else
return map->s_partition_root + block + offset;
}
uint32_t udf_get_pblock_virt15(struct super_block *sb, uint32_t block,
uint16_t partition, uint32_t offset)
{
struct buffer_head *bh = NULL;
uint32_t newblock;
uint32_t index;
uint32_t loc;
struct udf_sb_info *sbi = UDF_SB(sb);
struct udf_part_map *map;
struct udf_virtual_data *vdata;
struct udf_inode_info *iinfo = UDF_I(sbi->s_vat_inode);
int err;
map = &sbi->s_partmaps[partition];
vdata = &map->s_type_specific.s_virtual;
if (block > vdata->s_num_entries) {
udf_debug("Trying to access block beyond end of VAT (%u max %u)\n",
block, vdata->s_num_entries);
return 0xFFFFFFFF;
}
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
loc = le32_to_cpu(((__le32 *)(iinfo->i_data +
vdata->s_start_offset))[block]);
goto translate;
}
index = (sb->s_blocksize - vdata->s_start_offset) / sizeof(uint32_t);
if (block >= index) {
block -= index;
newblock = 1 + (block / (sb->s_blocksize / sizeof(uint32_t)));
index = block % (sb->s_blocksize / sizeof(uint32_t));
} else {
newblock = 0;
index = vdata->s_start_offset / sizeof(uint32_t) + block;
}
bh = udf_bread(sbi->s_vat_inode, newblock, 0, &err);
if (!bh) {
udf_debug("get_pblock(UDF_VIRTUAL_MAP:%p,%u,%u)\n",
sb, block, partition);
return 0xFFFFFFFF;
}
loc = le32_to_cpu(((__le32 *)bh->b_data)[index]);
brelse(bh);
translate:
if (iinfo->i_location.partitionReferenceNum == partition) {
udf_debug("recursive call to udf_get_pblock!\n");
return 0xFFFFFFFF;
}
return udf_get_pblock(sb, loc,
iinfo->i_location.partitionReferenceNum,
offset);
}
inline uint32_t udf_get_pblock_virt20(struct super_block *sb, uint32_t block,
uint16_t partition, uint32_t offset)
{
return udf_get_pblock_virt15(sb, block, partition, offset);
}
uint32_t udf_get_pblock_spar15(struct super_block *sb, uint32_t block,
uint16_t partition, uint32_t offset)
{
int i;
struct sparingTable *st = NULL;
struct udf_sb_info *sbi = UDF_SB(sb);
struct udf_part_map *map;
uint32_t packet;
struct udf_sparing_data *sdata;
map = &sbi->s_partmaps[partition];
sdata = &map->s_type_specific.s_sparing;
packet = (block + offset) & ~(sdata->s_packet_len - 1);
for (i = 0; i < 4; i++) {
if (sdata->s_spar_map[i] != NULL) {
st = (struct sparingTable *)
sdata->s_spar_map[i]->b_data;
break;
}
}
if (st) {
for (i = 0; i < le16_to_cpu(st->reallocationTableLen); i++) {
struct sparingEntry *entry = &st->mapEntry[i];
u32 origLoc = le32_to_cpu(entry->origLocation);
if (origLoc >= 0xFFFFFFF0)
break;
else if (origLoc == packet)
return le32_to_cpu(entry->mappedLocation) +
((block + offset) &
(sdata->s_packet_len - 1));
else if (origLoc > packet)
break;
}
}
return map->s_partition_root + block + offset;
}
int udf_relocate_blocks(struct super_block *sb, long old_block, long *new_block)
{
struct udf_sparing_data *sdata;
struct sparingTable *st = NULL;
struct sparingEntry mapEntry;
uint32_t packet;
int i, j, k, l;
struct udf_sb_info *sbi = UDF_SB(sb);
u16 reallocationTableLen;
struct buffer_head *bh;
int ret = 0;
mutex_lock(&sbi->s_alloc_mutex);
for (i = 0; i < sbi->s_partitions; i++) {
struct udf_part_map *map = &sbi->s_partmaps[i];
if (old_block > map->s_partition_root &&
old_block < map->s_partition_root + map->s_partition_len) {
sdata = &map->s_type_specific.s_sparing;
packet = (old_block - map->s_partition_root) &
~(sdata->s_packet_len - 1);
for (j = 0; j < 4; j++)
if (sdata->s_spar_map[j] != NULL) {
st = (struct sparingTable *)
sdata->s_spar_map[j]->b_data;
break;
}
if (!st) {
ret = 1;
goto out;
}
reallocationTableLen =
le16_to_cpu(st->reallocationTableLen);
for (k = 0; k < reallocationTableLen; k++) {
struct sparingEntry *entry = &st->mapEntry[k];
u32 origLoc = le32_to_cpu(entry->origLocation);
if (origLoc == 0xFFFFFFFF) {
for (; j < 4; j++) {
int len;
bh = sdata->s_spar_map[j];
if (!bh)
continue;
st = (struct sparingTable *)
bh->b_data;
entry->origLocation =
cpu_to_le32(packet);
len =
sizeof(struct sparingTable) +
reallocationTableLen *
sizeof(struct sparingEntry);
udf_update_tag((char *)st, len);
mark_buffer_dirty(bh);
}
*new_block = le32_to_cpu(
entry->mappedLocation) +
((old_block -
map->s_partition_root) &
(sdata->s_packet_len - 1));
ret = 0;
goto out;
} else if (origLoc == packet) {
*new_block = le32_to_cpu(
entry->mappedLocation) +
((old_block -
map->s_partition_root) &
(sdata->s_packet_len - 1));
ret = 0;
goto out;
} else if (origLoc > packet)
break;
}
for (l = k; l < reallocationTableLen; l++) {
struct sparingEntry *entry = &st->mapEntry[l];
u32 origLoc = le32_to_cpu(entry->origLocation);
if (origLoc != 0xFFFFFFFF)
continue;
for (; j < 4; j++) {
bh = sdata->s_spar_map[j];
if (!bh)
continue;
st = (struct sparingTable *)bh->b_data;
mapEntry = st->mapEntry[l];
mapEntry.origLocation =
cpu_to_le32(packet);
memmove(&st->mapEntry[k + 1],
&st->mapEntry[k],
(l - k) *
sizeof(struct sparingEntry));
st->mapEntry[k] = mapEntry;
udf_update_tag((char *)st,
sizeof(struct sparingTable) +
reallocationTableLen *
sizeof(struct sparingEntry));
mark_buffer_dirty(bh);
}
*new_block =
le32_to_cpu(
st->mapEntry[k].mappedLocation) +
((old_block - map->s_partition_root) &
(sdata->s_packet_len - 1));
ret = 0;
goto out;
}
ret = 1;
goto out;
} /* if old_block */
}
if (i == sbi->s_partitions) {
/* outside of partitions */
/* for now, fail =) */
ret = 1;
}
out:
mutex_unlock(&sbi->s_alloc_mutex);
return ret;
}
static uint32_t udf_try_read_meta(struct inode *inode, uint32_t block,
uint16_t partition, uint32_t offset)
{
struct super_block *sb = inode->i_sb;
struct udf_part_map *map;
struct kernel_lb_addr eloc;
uint32_t elen;
sector_t ext_offset;
struct extent_position epos = {};
uint32_t phyblock;
int8_t etype;
int err = 0;
err = inode_bmap(inode, block, &epos, &eloc, &elen, &ext_offset, &etype);
if (err <= 0 || etype != (EXT_RECORDED_ALLOCATED >> 30))
phyblock = 0xFFFFFFFF;
else {
map = &UDF_SB(sb)->s_partmaps[partition];
/* map to sparable/physical partition desc */
phyblock = udf_get_pblock(sb, eloc.logicalBlockNum,
map->s_type_specific.s_metadata.s_phys_partition_ref,
ext_offset + offset);
}
brelse(epos.bh);
return phyblock;
}
uint32_t udf_get_pblock_meta25(struct super_block *sb, uint32_t block,
uint16_t partition, uint32_t offset)
{
struct udf_sb_info *sbi = UDF_SB(sb);
struct udf_part_map *map;
struct udf_meta_data *mdata;
uint32_t retblk;
struct inode *inode;
udf_debug("READING from METADATA\n");
map = &sbi->s_partmaps[partition];
mdata = &map->s_type_specific.s_metadata;
inode = mdata->s_metadata_fe ? : mdata->s_mirror_fe;
if (!inode)
return 0xFFFFFFFF;
retblk = udf_try_read_meta(inode, block, partition, offset);
if (retblk == 0xFFFFFFFF && mdata->s_metadata_fe) {
udf_warn(sb, "error reading from METADATA, trying to read from MIRROR\n");
if (!(mdata->s_flags & MF_MIRROR_FE_LOADED)) {
mdata->s_mirror_fe = udf_find_metadata_inode_efe(sb,
mdata->s_mirror_file_loc,
mdata->s_phys_partition_ref);
if (IS_ERR(mdata->s_mirror_fe))
mdata->s_mirror_fe = NULL;
mdata->s_flags |= MF_MIRROR_FE_LOADED;
}
inode = mdata->s_mirror_fe;
if (!inode)
return 0xFFFFFFFF;
retblk = udf_try_read_meta(inode, block, partition, offset);
}
return retblk;
}
File diff suppressed because it is too large Load Diff
+171
View File
@@ -0,0 +1,171 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* symlink.c
*
* PURPOSE
* Symlink handling routines for the OSTA-UDF(tm) filesystem.
*
* COPYRIGHT
* (C) 1998-2001 Ben Fennema
* (C) 1999 Stelias Computing Inc
*
* HISTORY
*
* 04/16/99 blf Created.
*
*/
#include "udfdecl.h"
#include <linux/uaccess.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/time.h>
#include <linux/mm.h>
#include <linux/stat.h>
#include <linux/pagemap.h>
#include "udf_i.h"
static int udf_pc_to_char(struct super_block *sb, unsigned char *from,
int fromlen, unsigned char *to, int tolen)
{
struct pathComponent *pc;
int elen = 0;
int comp_len;
unsigned char *p = to;
/* Reserve one byte for terminating \0 */
tolen--;
while (elen < fromlen) {
pc = (struct pathComponent *)(from + elen);
elen += sizeof(struct pathComponent);
switch (pc->componentType) {
case 1:
/*
* Symlink points to some place which should be agreed
* upon between originator and receiver of the media. Ignore.
*/
if (pc->lengthComponentIdent > 0) {
elen += pc->lengthComponentIdent;
break;
}
fallthrough;
case 2:
if (tolen == 0)
return -ENAMETOOLONG;
p = to;
*p++ = '/';
tolen--;
break;
case 3:
if (tolen < 3)
return -ENAMETOOLONG;
memcpy(p, "../", 3);
p += 3;
tolen -= 3;
break;
case 4:
if (tolen < 2)
return -ENAMETOOLONG;
memcpy(p, "./", 2);
p += 2;
tolen -= 2;
/* that would be . - just ignore */
break;
case 5:
elen += pc->lengthComponentIdent;
if (elen > fromlen)
return -EIO;
comp_len = udf_get_filename(sb, pc->componentIdent,
pc->lengthComponentIdent,
p, tolen);
if (comp_len < 0)
return comp_len;
p += comp_len;
tolen -= comp_len;
if (tolen == 0)
return -ENAMETOOLONG;
*p++ = '/';
tolen--;
break;
}
}
if (p > to + 1)
p[-1] = '\0';
else
p[0] = '\0';
return 0;
}
static int udf_symlink_filler(struct file *file, struct folio *folio)
{
struct inode *inode = folio->mapping->host;
struct buffer_head *bh = NULL;
unsigned char *symlink;
int err = 0;
unsigned char *p = folio_address(folio);
struct udf_inode_info *iinfo = UDF_I(inode);
/* We don't support symlinks longer than one block */
if (inode->i_size > inode->i_sb->s_blocksize) {
err = -ENAMETOOLONG;
goto out;
}
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
symlink = iinfo->i_data + iinfo->i_lenEAttr;
} else {
bh = udf_bread(inode, 0, 0, &err);
if (!bh) {
if (!err)
err = -EFSCORRUPTED;
goto out;
}
symlink = bh->b_data;
}
err = udf_pc_to_char(inode->i_sb, symlink, inode->i_size, p, PAGE_SIZE);
brelse(bh);
out:
folio_end_read(folio, err == 0);
return err;
}
static int udf_symlink_getattr(struct mnt_idmap *idmap,
const struct path *path, struct kstat *stat,
u32 request_mask, unsigned int flags)
{
struct dentry *dentry = path->dentry;
struct inode *inode = d_backing_inode(dentry);
struct folio *folio;
generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
folio = read_mapping_folio(inode->i_mapping, 0, NULL);
if (IS_ERR(folio))
return PTR_ERR(folio);
/*
* UDF uses non-trivial encoding of symlinks so i_size does not match
* number of characters reported by readlink(2) which apparently some
* applications expect. Also POSIX says that "The value returned in the
* st_size field shall be the length of the contents of the symbolic
* link, and shall not count a trailing null if one is present." So
* let's report the length of string returned by readlink(2) for
* st_size.
*/
stat->size = strlen(folio_address(folio));
folio_put(folio);
return 0;
}
/*
* symlinks can't do much...
*/
const struct address_space_operations udf_symlink_aops = {
.read_folio = udf_symlink_filler,
};
const struct inode_operations udf_symlink_inode_operations = {
.get_link = page_get_link,
.getattr = udf_symlink_getattr,
};
+293
View File
@@ -0,0 +1,293 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* truncate.c
*
* PURPOSE
* Truncate handling routines for the OSTA-UDF(tm) filesystem.
*
* COPYRIGHT
* (C) 1999-2004 Ben Fennema
* (C) 1999 Stelias Computing Inc
*
* HISTORY
*
* 02/24/99 blf Created.
*
*/
#include "udfdecl.h"
#include <linux/fs.h>
#include <linux/mm.h>
#include "udf_i.h"
#include "udf_sb.h"
static void extent_trunc(struct inode *inode, struct extent_position *epos,
struct kernel_lb_addr *eloc, int8_t etype, uint32_t elen,
uint32_t nelen)
{
struct kernel_lb_addr neloc = {};
int last_block = (elen + inode->i_sb->s_blocksize - 1) >>
inode->i_sb->s_blocksize_bits;
int first_block = (nelen + inode->i_sb->s_blocksize - 1) >>
inode->i_sb->s_blocksize_bits;
if (nelen) {
if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) {
udf_free_blocks(inode->i_sb, inode, eloc, 0,
last_block);
etype = (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30);
} else
neloc = *eloc;
nelen = (etype << 30) | nelen;
}
if (elen != nelen) {
udf_write_aext(inode, epos, &neloc, nelen, 0);
if (last_block > first_block) {
if (etype == (EXT_RECORDED_ALLOCATED >> 30))
mark_inode_dirty(inode);
if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
udf_free_blocks(inode->i_sb, inode, eloc,
first_block,
last_block - first_block);
}
}
}
/*
* Truncate the last extent to match i_size. This function assumes
* that preallocation extent is already truncated.
*/
void udf_truncate_tail_extent(struct inode *inode)
{
struct extent_position epos = {};
struct kernel_lb_addr eloc;
uint32_t elen, nelen;
uint64_t lbcount = 0;
int8_t etype = -1, netype;
int adsize;
struct udf_inode_info *iinfo = UDF_I(inode);
int ret;
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB ||
inode->i_size == iinfo->i_lenExtents)
return;
/* Are we going to delete the file anyway? */
if (inode->i_nlink == 0)
return;
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
adsize = sizeof(struct short_ad);
else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
adsize = sizeof(struct long_ad);
else
BUG();
/* Find the last extent in the file */
while (1) {
ret = udf_next_aext(inode, &epos, &eloc, &elen, &netype, 1);
if (ret <= 0)
break;
etype = netype;
lbcount += elen;
if (lbcount > inode->i_size) {
if (lbcount - inode->i_size >= inode->i_sb->s_blocksize)
udf_warn(inode->i_sb,
"Too long extent after EOF in inode %u: i_size: %lld lbcount: %lld extent %u+%u\n",
(unsigned)inode->i_ino,
(long long)inode->i_size,
(long long)lbcount,
(unsigned)eloc.logicalBlockNum,
(unsigned)elen);
nelen = elen - (lbcount - inode->i_size);
epos.offset -= adsize;
extent_trunc(inode, &epos, &eloc, etype, elen, nelen);
epos.offset += adsize;
if (udf_next_aext(inode, &epos, &eloc, &elen,
&netype, 1) > 0)
udf_err(inode->i_sb,
"Extent after EOF in inode %u\n",
(unsigned)inode->i_ino);
break;
}
}
/* This inode entry is in-memory only and thus we don't have to mark
* the inode dirty */
if (ret >= 0)
iinfo->i_lenExtents = inode->i_size;
brelse(epos.bh);
}
void udf_discard_prealloc(struct inode *inode)
{
struct extent_position epos = {};
struct extent_position prev_epos = {};
struct kernel_lb_addr eloc;
uint32_t elen;
uint64_t lbcount = 0;
int8_t etype = -1;
struct udf_inode_info *iinfo = UDF_I(inode);
int bsize = i_blocksize(inode);
int8_t tmpetype = -1;
int ret;
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB ||
ALIGN(inode->i_size, bsize) == ALIGN(iinfo->i_lenExtents, bsize))
return;
epos.block = iinfo->i_location;
/* Find the last extent in the file */
while (1) {
ret = udf_next_aext(inode, &epos, &eloc, &elen, &tmpetype, 0);
if (ret < 0)
goto out;
if (ret == 0)
break;
brelse(prev_epos.bh);
prev_epos = epos;
if (prev_epos.bh)
get_bh(prev_epos.bh);
ret = udf_next_aext(inode, &epos, &eloc, &elen, &etype, 1);
if (ret < 0)
goto out;
lbcount += elen;
}
if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) {
lbcount -= elen;
udf_delete_aext(inode, prev_epos);
udf_free_blocks(inode->i_sb, inode, &eloc, 0,
DIV_ROUND_UP(elen, bsize));
}
/* This inode entry is in-memory only and thus we don't have to mark
* the inode dirty */
iinfo->i_lenExtents = lbcount;
out:
brelse(epos.bh);
brelse(prev_epos.bh);
}
static void udf_update_alloc_ext_desc(struct inode *inode,
struct extent_position *epos,
u32 lenalloc)
{
struct super_block *sb = inode->i_sb;
struct udf_sb_info *sbi = UDF_SB(sb);
struct allocExtDesc *aed = (struct allocExtDesc *) (epos->bh->b_data);
int len = sizeof(struct allocExtDesc);
aed->lengthAllocDescs = cpu_to_le32(lenalloc);
if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT) || sbi->s_udfrev >= 0x0201)
len += lenalloc;
udf_update_tag(epos->bh->b_data, len);
mmb_mark_buffer_dirty(epos->bh, &UDF_I(inode)->i_metadata_bhs);
}
/*
* Truncate extents of inode to inode->i_size. This function can be used only
* for making file shorter. For making file longer, udf_extend_file() has to
* be used.
*/
int udf_truncate_extents(struct inode *inode)
{
struct extent_position epos;
struct kernel_lb_addr eloc, neloc = {};
uint32_t elen, nelen = 0, indirect_ext_len = 0, lenalloc;
int8_t etype;
struct super_block *sb = inode->i_sb;
sector_t first_block = inode->i_size >> sb->s_blocksize_bits, offset;
loff_t byte_offset;
int adsize;
struct udf_inode_info *iinfo = UDF_I(inode);
int ret = 0;
if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
adsize = sizeof(struct short_ad);
else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
adsize = sizeof(struct long_ad);
else
BUG();
ret = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset, &etype);
if (ret < 0)
return ret;
byte_offset = (offset << sb->s_blocksize_bits) +
(inode->i_size & (sb->s_blocksize - 1));
if (ret == 0) {
/* We should extend the file? */
WARN_ON(byte_offset);
return 0;
}
epos.offset -= adsize;
extent_trunc(inode, &epos, &eloc, etype, elen, byte_offset);
epos.offset += adsize;
if (byte_offset)
lenalloc = epos.offset;
else
lenalloc = epos.offset - adsize;
if (!epos.bh)
lenalloc -= udf_file_entry_alloc_offset(inode);
else
lenalloc -= sizeof(struct allocExtDesc);
while ((ret = udf_current_aext(inode, &epos, &eloc,
&elen, &etype, 0)) > 0) {
if (etype == (EXT_NEXT_EXTENT_ALLOCDESCS >> 30)) {
udf_write_aext(inode, &epos, &neloc, nelen, 0);
if (indirect_ext_len) {
/* We managed to free all extents in the
* indirect extent - free it too */
BUG_ON(!epos.bh);
udf_free_blocks(sb, NULL, &epos.block,
0, indirect_ext_len);
} else if (!epos.bh) {
iinfo->i_lenAlloc = lenalloc;
mark_inode_dirty(inode);
} else
udf_update_alloc_ext_desc(inode,
&epos, lenalloc);
brelse(epos.bh);
epos.offset = sizeof(struct allocExtDesc);
epos.block = eloc;
epos.bh = sb_bread(sb,
udf_get_lb_pblock(sb, &eloc, 0));
/* Error reading indirect block? */
if (!epos.bh)
return -EIO;
if (elen)
indirect_ext_len =
(elen + sb->s_blocksize - 1) >>
sb->s_blocksize_bits;
else
indirect_ext_len = 1;
} else {
extent_trunc(inode, &epos, &eloc, etype, elen, 0);
epos.offset += adsize;
}
}
if (ret < 0) {
brelse(epos.bh);
return ret;
}
if (indirect_ext_len) {
BUG_ON(!epos.bh);
udf_free_blocks(sb, NULL, &epos.block, 0, indirect_ext_len);
} else if (!epos.bh) {
iinfo->i_lenAlloc = lenalloc;
mark_inode_dirty(inode);
} else
udf_update_alloc_ext_desc(inode, &epos, lenalloc);
iinfo->i_lenExtents = inode->i_size;
brelse(epos.bh);
return 0;
}
+65
View File
@@ -0,0 +1,65 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _UDF_I_H
#define _UDF_I_H
struct extent_position {
struct buffer_head *bh;
uint32_t offset;
struct kernel_lb_addr block;
};
struct udf_ext_cache {
/* Extent position */
struct extent_position epos;
/* Start logical offset in bytes */
loff_t lstart;
};
/*
* The i_data_sem and i_mutex serve for protection of allocation information
* of a regular files and symlinks. This includes all extents belonging to
* the file/symlink, a fact whether data are in-inode or in external data
* blocks, preallocation, goal block information... When extents are read,
* i_mutex or i_data_sem must be held (for reading is enough in case of
* i_data_sem). When extents are changed, i_data_sem must be held for writing
* and also i_mutex must be held.
*
* For directories i_mutex is used for all the necessary protection.
*/
struct udf_inode_info {
struct timespec64 i_crtime;
/* Physical address of inode */
struct kernel_lb_addr i_location;
__u64 i_unique;
__u32 i_lenEAttr;
__u32 i_lenAlloc;
__u64 i_lenExtents;
__u32 i_next_alloc_block;
__u32 i_next_alloc_goal;
__u32 i_checkpoint;
__u32 i_extraPerms;
unsigned i_alloc_type : 3;
unsigned i_efe : 1; /* extendedFileEntry */
unsigned i_use : 1; /* unallocSpaceEntry */
unsigned i_strat4096 : 1;
unsigned i_streamdir : 1;
unsigned i_hidden : 1; /* hidden system inode */
unsigned reserved : 24;
__u8 *i_data;
struct kernel_lb_addr i_locStreamdir;
__u64 i_lenStreams;
struct rw_semaphore i_data_sem;
struct mapping_metadata_bhs i_metadata_bhs;
struct udf_ext_cache cached_extent;
/* Spinlock for protecting extent cache */
spinlock_t i_extent_cache_lock;
struct inode vfs_inode;
};
static inline struct udf_inode_info *UDF_I(struct inode *inode)
{
return container_of(inode, struct udf_inode_info, vfs_inode);
}
#endif /* _UDF_I_H) */
+183
View File
@@ -0,0 +1,183 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __LINUX_UDF_SB_H
#define __LINUX_UDF_SB_H
#include <linux/mutex.h>
#include <linux/bitops.h>
#include <linux/magic.h>
/*
* Even UDF 2.6 media should have version <= 0x250 but apparently there are
* some broken filesystems with version set to 0x260. Accommodate those.
*/
#define UDF_MAX_READ_VERSION 0x0260
#define UDF_MAX_WRITE_VERSION 0x0201
#define UDF_FLAG_USE_EXTENDED_FE 0
#define UDF_VERS_USE_EXTENDED_FE 0x0200
#define UDF_FLAG_USE_STREAMS 1
#define UDF_VERS_USE_STREAMS 0x0200
#define UDF_FLAG_USE_SHORT_AD 2
#define UDF_FLAG_USE_AD_IN_ICB 3
#define UDF_FLAG_USE_FILE_CTIME_EA 4
#define UDF_FLAG_STRICT 5
#define UDF_FLAG_UNDELETE 6
#define UDF_FLAG_UNHIDE 7
#define UDF_FLAG_NOVRS 8
#define UDF_FLAG_UID_FORGET 11 /* save -1 for uid to disk */
#define UDF_FLAG_GID_FORGET 12
#define UDF_FLAG_UID_SET 13
#define UDF_FLAG_GID_SET 14
#define UDF_FLAG_SESSION_SET 15
#define UDF_FLAG_LASTBLOCK_SET 16
#define UDF_FLAG_BLOCKSIZE_SET 17
#define UDF_FLAG_INCONSISTENT 18
#define UDF_FLAG_RW_INCOMPAT 19 /* Set when we find RW incompatible
* feature */
#define UDF_PART_FLAG_UNALLOC_BITMAP 0x0001
#define UDF_PART_FLAG_UNALLOC_TABLE 0x0002
#define UDF_PART_FLAG_READ_ONLY 0x0010
#define UDF_PART_FLAG_WRITE_ONCE 0x0020
#define UDF_PART_FLAG_REWRITABLE 0x0040
#define UDF_PART_FLAG_OVERWRITABLE 0x0080
#define UDF_MAX_BLOCK_LOADED 8
#define UDF_TYPE1_MAP15 0x1511U
#define UDF_VIRTUAL_MAP15 0x1512U
#define UDF_VIRTUAL_MAP20 0x2012U
#define UDF_SPARABLE_MAP15 0x1522U
#define UDF_METADATA_MAP25 0x2511U
#define UDF_INVALID_MODE ((umode_t)-1)
#define MF_DUPLICATE_MD 0x01
#define MF_MIRROR_FE_LOADED 0x02
struct udf_meta_data {
__u32 s_meta_file_loc;
__u32 s_mirror_file_loc;
__u32 s_bitmap_file_loc;
__u32 s_alloc_unit_size;
__u16 s_align_unit_size;
/*
* Partition Reference Number of the associated physical / sparable
* partition
*/
__u16 s_phys_partition_ref;
int s_flags;
struct inode *s_metadata_fe;
struct inode *s_mirror_fe;
struct inode *s_bitmap_fe;
};
struct udf_sparing_data {
__u16 s_packet_len;
struct buffer_head *s_spar_map[4];
};
struct udf_virtual_data {
__u32 s_num_entries;
__u16 s_start_offset;
};
struct udf_bitmap {
__u32 s_extPosition;
int s_nr_groups;
struct buffer_head *s_block_bitmap[] __counted_by(s_nr_groups);
};
struct udf_part_map {
union {
struct udf_bitmap *s_bitmap;
struct inode *s_table;
} s_uspace;
__u32 s_partition_root;
__u32 s_partition_len;
__u16 s_partition_type;
__u16 s_partition_num;
union {
struct udf_sparing_data s_sparing;
struct udf_virtual_data s_virtual;
struct udf_meta_data s_metadata;
} s_type_specific;
__u32 (*s_partition_func)(struct super_block *, __u32, __u16, __u32);
__u16 s_volumeseqnum;
__u16 s_partition_flags;
};
#pragma pack()
struct udf_sb_info {
struct udf_part_map *s_partmaps;
__u8 s_volume_ident[32];
/* Overall info */
__u16 s_partitions;
__u16 s_partition;
/* Sector headers */
__s32 s_session;
__u32 s_anchor;
__u32 s_last_block;
struct buffer_head *s_lvid_bh;
/* Default permissions */
umode_t s_umask;
kgid_t s_gid;
kuid_t s_uid;
umode_t s_fmode;
umode_t s_dmode;
/* Lock protecting consistency of above permission settings */
rwlock_t s_cred_lock;
/* Root Info */
struct timespec64 s_record_time;
/* Fileset Info */
__u16 s_serial_number;
/* highest UDF revision we have recorded to this media */
__u16 s_udfrev;
/* Miscellaneous flags */
unsigned long s_flags;
/* Encoding info */
struct nls_table *s_nls_map;
/* VAT inode */
struct inode *s_vat_inode;
struct mutex s_alloc_mutex;
/* Protected by s_alloc_mutex */
unsigned int s_lvid_dirty;
};
static inline struct udf_sb_info *UDF_SB(struct super_block *sb)
{
return sb->s_fs_info;
}
struct logicalVolIntegrityDescImpUse *udf_sb_lvidiu(struct super_block *sb);
int udf_compute_nr_groups(struct super_block *sb, u32 partition);
static inline int UDF_QUERY_FLAG(struct super_block *sb, int flag)
{
return test_bit(flag, &UDF_SB(sb)->s_flags);
}
static inline void UDF_SET_FLAG(struct super_block *sb, int flag)
{
set_bit(flag, &UDF_SB(sb)->s_flags);
}
static inline void UDF_CLEAR_FLAG(struct super_block *sb, int flag)
{
clear_bit(flag, &UDF_SB(sb)->s_flags);
}
#endif /* __LINUX_UDF_SB_H */
+261
View File
@@ -0,0 +1,261 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __UDF_DECL_H
#define __UDF_DECL_H
#define pr_fmt(fmt) "UDF-fs: " fmt
#include "ecma_167.h"
#include "osta_udf.h"
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/buffer_head.h>
#include <linux/udf_fs_i.h>
#include "udf_sb.h"
#include "udfend.h"
#include "udf_i.h"
#define UDF_DEFAULT_PREALLOC_BLOCKS 8
extern __printf(3, 4) void _udf_err(struct super_block *sb,
const char *function, const char *fmt, ...);
#define udf_err(sb, fmt, ...) \
_udf_err(sb, __func__, fmt, ##__VA_ARGS__)
extern __printf(3, 4) void _udf_warn(struct super_block *sb,
const char *function, const char *fmt, ...);
#define udf_warn(sb, fmt, ...) \
_udf_warn(sb, __func__, fmt, ##__VA_ARGS__)
#define udf_info(fmt, ...) \
pr_info("INFO " fmt, ##__VA_ARGS__)
#define udf_debug(fmt, ...) \
pr_debug("%s:%d:%s: " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
#define UDF_EXTENT_LENGTH_MASK 0x3FFFFFFF
#define UDF_EXTENT_FLAG_MASK 0xC0000000
#define UDF_INVALID_ID ((uint32_t)-1)
#define UDF_NAME_PAD 4
#define UDF_NAME_LEN 254
#define UDF_NAME_LEN_CS0 255
static inline size_t udf_file_entry_alloc_offset(struct inode *inode)
{
struct udf_inode_info *iinfo = UDF_I(inode);
if (iinfo->i_use)
return sizeof(struct unallocSpaceEntry);
else if (iinfo->i_efe)
return sizeof(struct extendedFileEntry) + iinfo->i_lenEAttr;
else
return sizeof(struct fileEntry) + iinfo->i_lenEAttr;
}
static inline size_t udf_ext0_offset(struct inode *inode)
{
if (UDF_I(inode)->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB)
return udf_file_entry_alloc_offset(inode);
else
return 0;
}
/* computes tag checksum */
u8 udf_tag_checksum(const struct tag *t);
typedef uint32_t udf_pblk_t;
struct dentry;
struct inode;
struct task_struct;
struct buffer_head;
struct super_block;
extern const struct export_operations udf_export_ops;
extern const struct inode_operations udf_dir_inode_operations;
extern const struct file_operations udf_dir_operations;
extern const struct inode_operations udf_file_inode_operations;
extern const struct file_operations udf_file_operations;
extern const struct inode_operations udf_symlink_inode_operations;
extern const struct address_space_operations udf_aops;
extern const struct address_space_operations udf_symlink_aops;
struct udf_fileident_iter {
struct inode *dir; /* Directory we are working with */
loff_t pos; /* Logical position in a dir */
struct buffer_head *bh[2]; /* Buffer containing 'pos' and possibly
* next buffer if entry straddles
* blocks */
struct kernel_lb_addr eloc; /* Start of extent containing 'pos' */
uint32_t elen; /* Length of extent containing 'pos' */
sector_t loffset; /* Block offset of 'pos' within above
* extent */
struct extent_position epos; /* Position after the above extent */
struct fileIdentDesc fi; /* Copied directory entry */
uint8_t *name; /* Pointer to entry name */
uint8_t *namebuf; /* Storage for entry name in case
* the name is split between two blocks
*/
};
struct udf_vds_record {
uint32_t block;
uint32_t volDescSeqNum;
};
struct generic_desc {
struct tag descTag;
__le32 volDescSeqNum;
};
/* super.c */
static inline void udf_updated_lvid(struct super_block *sb)
{
struct buffer_head *bh = UDF_SB(sb)->s_lvid_bh;
BUG_ON(!bh);
WARN_ON_ONCE(((struct logicalVolIntegrityDesc *)
bh->b_data)->integrityType !=
cpu_to_le32(LVID_INTEGRITY_TYPE_OPEN));
UDF_SB(sb)->s_lvid_dirty = 1;
}
extern u64 lvid_get_unique_id(struct super_block *sb);
struct inode *udf_find_metadata_inode_efe(struct super_block *sb,
u32 meta_file_loc, u32 partition_num);
/* namei.c */
static inline unsigned int udf_dir_entry_len(struct fileIdentDesc *cfi)
{
return ALIGN(sizeof(struct fileIdentDesc) +
le16_to_cpu(cfi->lengthOfImpUse) + cfi->lengthFileIdent,
UDF_NAME_PAD);
}
/* file.c */
extern long udf_ioctl(struct file *, unsigned int, unsigned long);
int udf_fsync(struct file *file, loff_t start, loff_t end, int datasync);
/* inode.c */
extern struct inode *__udf_iget(struct super_block *, struct kernel_lb_addr *,
bool hidden_inode);
static inline struct inode *udf_iget_special(struct super_block *sb,
struct kernel_lb_addr *ino)
{
return __udf_iget(sb, ino, true);
}
static inline struct inode *udf_iget(struct super_block *sb,
struct kernel_lb_addr *ino)
{
return __udf_iget(sb, ino, false);
}
extern int udf_expand_file_adinicb(struct inode *);
extern struct buffer_head *udf_bread(struct inode *inode, udf_pblk_t block,
int create, int *err);
extern int udf_setsize(struct inode *, loff_t);
extern void udf_evict_inode(struct inode *);
extern int udf_write_inode(struct inode *, struct writeback_control *wbc);
extern int inode_bmap(struct inode *inode, sector_t block,
struct extent_position *pos, struct kernel_lb_addr *eloc,
uint32_t *elen, sector_t *offset, int8_t *etype);
int udf_get_block(struct inode *, sector_t, struct buffer_head *, int);
extern int udf_setup_indirect_aext(struct inode *inode, udf_pblk_t block,
struct extent_position *epos);
extern int __udf_add_aext(struct inode *inode, struct extent_position *epos,
struct kernel_lb_addr *eloc, uint32_t elen, int inc);
extern int udf_add_aext(struct inode *, struct extent_position *,
struct kernel_lb_addr *, uint32_t, int);
extern void udf_write_aext(struct inode *, struct extent_position *,
struct kernel_lb_addr *, uint32_t, int);
extern int8_t udf_delete_aext(struct inode *, struct extent_position);
extern int udf_next_aext(struct inode *inode, struct extent_position *epos,
struct kernel_lb_addr *eloc, uint32_t *elen,
int8_t *etype, int inc);
extern int udf_current_aext(struct inode *inode, struct extent_position *epos,
struct kernel_lb_addr *eloc, uint32_t *elen,
int8_t *etype, int inc);
extern void udf_update_extra_perms(struct inode *inode, umode_t mode);
/* misc.c */
extern struct genericFormat *udf_add_extendedattr(struct inode *, uint32_t,
uint32_t, uint8_t);
extern struct genericFormat *udf_get_extendedattr(struct inode *, uint32_t,
uint8_t);
extern struct buffer_head *udf_read_tagged(struct super_block *, uint32_t,
uint32_t, uint16_t *);
extern struct buffer_head *udf_read_ptagged(struct super_block *,
struct kernel_lb_addr *, uint32_t,
uint16_t *);
extern void udf_update_tag(char *, int);
extern void udf_new_tag(char *, uint16_t, uint16_t, uint16_t, uint32_t, int);
/* lowlevel.c */
extern unsigned int udf_get_last_session(struct super_block *);
udf_pblk_t udf_get_last_block(struct super_block *);
/* partition.c */
extern uint32_t udf_get_pblock(struct super_block *, uint32_t, uint16_t,
uint32_t);
extern uint32_t udf_get_pblock_virt15(struct super_block *, uint32_t, uint16_t,
uint32_t);
extern uint32_t udf_get_pblock_virt20(struct super_block *, uint32_t, uint16_t,
uint32_t);
extern uint32_t udf_get_pblock_spar15(struct super_block *, uint32_t, uint16_t,
uint32_t);
extern uint32_t udf_get_pblock_meta25(struct super_block *, uint32_t, uint16_t,
uint32_t);
extern int udf_relocate_blocks(struct super_block *, long, long *);
static inline uint32_t
udf_get_lb_pblock(struct super_block *sb, struct kernel_lb_addr *loc,
uint32_t offset)
{
return udf_get_pblock(sb, loc->logicalBlockNum,
loc->partitionReferenceNum, offset);
}
/* unicode.c */
extern int udf_get_filename(struct super_block *, const uint8_t *, int,
uint8_t *, int);
extern int udf_put_filename(struct super_block *, const uint8_t *, int,
uint8_t *, int);
extern int udf_dstrCS0toChar(struct super_block *, uint8_t *, int,
const uint8_t *, int);
/* ialloc.c */
extern void udf_free_inode(struct inode *);
extern struct inode *udf_new_inode(struct inode *, umode_t);
/* truncate.c */
extern void udf_truncate_tail_extent(struct inode *);
extern void udf_discard_prealloc(struct inode *);
extern int udf_truncate_extents(struct inode *);
/* balloc.c */
extern void udf_free_blocks(struct super_block *, struct inode *,
struct kernel_lb_addr *, uint32_t, uint32_t);
extern int udf_prealloc_blocks(struct super_block *, struct inode *, uint16_t,
uint32_t, uint32_t);
extern udf_pblk_t udf_new_block(struct super_block *sb, struct inode *inode,
uint16_t partition, uint32_t goal, int *err);
/* directory.c */
int udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir,
loff_t pos);
int udf_fiiter_advance(struct udf_fileident_iter *iter);
void udf_fiiter_release(struct udf_fileident_iter *iter);
void udf_fiiter_write_fi(struct udf_fileident_iter *iter, uint8_t *impuse);
void udf_fiiter_update_elen(struct udf_fileident_iter *iter, uint32_t new_elen);
int udf_fiiter_append_blk(struct udf_fileident_iter *iter);
extern struct long_ad *udf_get_filelongad(uint8_t *, int, uint32_t *, int);
extern struct short_ad *udf_get_fileshortad(uint8_t *, int, uint32_t *, int);
/* udftime.c */
extern void udf_disk_stamp_to_time(struct timespec64 *dest,
struct timestamp src);
extern void udf_time_to_disk_stamp(struct timestamp *dest, struct timespec64 src);
#endif /* __UDF_DECL_H */
+78
View File
@@ -0,0 +1,78 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __UDF_ENDIAN_H
#define __UDF_ENDIAN_H
#include <asm/byteorder.h>
#include <linux/string.h>
static inline struct kernel_lb_addr lelb_to_cpu(struct lb_addr in)
{
struct kernel_lb_addr out;
out.logicalBlockNum = le32_to_cpu(in.logicalBlockNum);
out.partitionReferenceNum = le16_to_cpu(in.partitionReferenceNum);
return out;
}
static inline struct lb_addr cpu_to_lelb(struct kernel_lb_addr in)
{
struct lb_addr out;
out.logicalBlockNum = cpu_to_le32(in.logicalBlockNum);
out.partitionReferenceNum = cpu_to_le16(in.partitionReferenceNum);
return out;
}
static inline struct short_ad lesa_to_cpu(struct short_ad in)
{
struct short_ad out;
out.extLength = le32_to_cpu(in.extLength);
out.extPosition = le32_to_cpu(in.extPosition);
return out;
}
static inline struct short_ad cpu_to_lesa(struct short_ad in)
{
struct short_ad out;
out.extLength = cpu_to_le32(in.extLength);
out.extPosition = cpu_to_le32(in.extPosition);
return out;
}
static inline struct kernel_long_ad lela_to_cpu(struct long_ad in)
{
struct kernel_long_ad out;
out.extLength = le32_to_cpu(in.extLength);
out.extLocation = lelb_to_cpu(in.extLocation);
return out;
}
static inline struct long_ad cpu_to_lela(struct kernel_long_ad in)
{
struct long_ad out;
out.extLength = cpu_to_le32(in.extLength);
out.extLocation = cpu_to_lelb(in.extLocation);
return out;
}
static inline struct kernel_extent_ad leea_to_cpu(struct extent_ad in)
{
struct kernel_extent_ad out;
out.extLength = le32_to_cpu(in.extLength);
out.extLocation = le32_to_cpu(in.extLocation);
return out;
}
#endif /* __UDF_ENDIAN_H */
+89
View File
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: LGPL-2.0+
/* Copyright (C) 1993, 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Paul Eggert (eggert@twinsun.com). */
/*
* dgb 10/02/98: ripped this from glibc source to help convert timestamps
* to unix time
* 10/04/98: added new table-based lookup after seeing how ugly
* the gnu code is
* blf 09/27/99: ripped out all the old code and inserted new table from
* John Brockmeyer (without leap second corrections)
* rewrote udf_stamp_to_time and fixed timezone accounting in
* udf_time_to_stamp.
*/
/*
* We don't take into account leap seconds. This may be correct or incorrect.
* For more NIST information (especially dealing with leap seconds), see:
* http://www.boulder.nist.gov/timefreq/pubs/bulletin/leapsecond.htm
*/
#include "udfdecl.h"
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/time.h>
void
udf_disk_stamp_to_time(struct timespec64 *dest, struct timestamp src)
{
u16 typeAndTimezone = le16_to_cpu(src.typeAndTimezone);
u16 year = le16_to_cpu(src.year);
uint8_t type = typeAndTimezone >> 12;
int16_t offset;
if (type == 1) {
offset = typeAndTimezone << 4;
/* sign extent offset */
offset = (offset >> 4);
if (offset == -2047) /* unspecified offset */
offset = 0;
} else
offset = 0;
dest->tv_sec = mktime64(year, src.month, src.day, src.hour, src.minute,
src.second);
dest->tv_sec -= offset * 60;
/*
* Sanitize nanosecond field since reportedly some filesystems are
* recorded with bogus sub-second values.
*/
if (src.centiseconds < 100 && src.hundredsOfMicroseconds < 100 &&
src.microseconds < 100) {
dest->tv_nsec = 1000 * (src.centiseconds * 10000 +
src.hundredsOfMicroseconds * 100 + src.microseconds);
} else {
dest->tv_nsec = 0;
}
}
void
udf_time_to_disk_stamp(struct timestamp *dest, struct timespec64 ts)
{
time64_t seconds;
int16_t offset;
struct tm tm;
offset = -sys_tz.tz_minuteswest;
dest->typeAndTimezone = cpu_to_le16(0x1000 | (offset & 0x0FFF));
seconds = ts.tv_sec + offset * 60;
time64_to_tm(seconds, 0, &tm);
dest->year = cpu_to_le16(tm.tm_year + 1900);
dest->month = tm.tm_mon + 1;
dest->day = tm.tm_mday;
dest->hour = tm.tm_hour;
dest->minute = tm.tm_min;
dest->second = tm.tm_sec;
dest->centiseconds = ts.tv_nsec / 10000000;
dest->hundredsOfMicroseconds = (ts.tv_nsec / 1000 -
dest->centiseconds * 10000) / 100;
dest->microseconds = (ts.tv_nsec / 1000 - dest->centiseconds * 10000 -
dest->hundredsOfMicroseconds * 100);
}
/* EOF */
+399
View File
@@ -0,0 +1,399 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* unicode.c
*
* PURPOSE
* Routines for converting between UTF-8 and OSTA Compressed Unicode.
* Also handles filename mangling
*
* DESCRIPTION
* OSTA Compressed Unicode is explained in the OSTA UDF specification.
* http://www.osta.org/
* UTF-8 is explained in the IETF RFC XXXX.
* ftp://ftp.internic.net/rfc/rfcxxxx.txt
*
*/
#include "udfdecl.h"
#include <linux/hex.h>
#include <linux/kernel.h>
#include <linux/string.h> /* for memset */
#include <linux/nls.h>
#include <linux/crc-itu-t.h>
#include <linux/slab.h>
#include "udf_sb.h"
#define PLANE_SIZE 0x10000
#define UNICODE_MAX 0x10ffff
#define SURROGATE_MASK 0xfffff800
#define SURROGATE_PAIR 0x0000d800
#define SURROGATE_LOW 0x00000400
#define SURROGATE_CHAR_BITS 10
#define SURROGATE_CHAR_MASK ((1 << SURROGATE_CHAR_BITS) - 1)
#define ILLEGAL_CHAR_MARK '_'
#define EXT_MARK '.'
#define CRC_MARK '#'
#define EXT_SIZE 5
/* Number of chars we need to store generated CRC to make filename unique */
#define CRC_LEN 5
static unicode_t get_utf16_char(const uint8_t *str_i, int str_i_max_len,
int str_i_idx, int u_ch, unicode_t *ret)
{
unicode_t c;
int start_idx = str_i_idx;
/* Expand OSTA compressed Unicode to Unicode */
c = str_i[str_i_idx++];
if (u_ch > 1)
c = (c << 8) | str_i[str_i_idx++];
if ((c & SURROGATE_MASK) == SURROGATE_PAIR) {
unicode_t next;
/* Trailing surrogate char */
if (str_i_idx >= str_i_max_len) {
c = UNICODE_MAX + 1;
goto out;
}
/* Low surrogate must follow the high one... */
if (c & SURROGATE_LOW) {
c = UNICODE_MAX + 1;
goto out;
}
WARN_ON_ONCE(u_ch != 2);
next = str_i[str_i_idx++] << 8;
next |= str_i[str_i_idx++];
if ((next & SURROGATE_MASK) != SURROGATE_PAIR ||
!(next & SURROGATE_LOW)) {
c = UNICODE_MAX + 1;
goto out;
}
c = PLANE_SIZE +
((c & SURROGATE_CHAR_MASK) << SURROGATE_CHAR_BITS) +
(next & SURROGATE_CHAR_MASK);
}
out:
*ret = c;
return str_i_idx - start_idx;
}
static int udf_name_conv_char(uint8_t *str_o, int str_o_max_len,
int *str_o_idx,
const uint8_t *str_i, int str_i_max_len,
int *str_i_idx,
int u_ch, int *needsCRC,
int (*conv_f)(wchar_t, unsigned char *, int),
int translate)
{
unicode_t c;
int illChar = 0;
int len, gotch = 0;
while (!gotch && *str_i_idx < str_i_max_len) {
if (*str_o_idx >= str_o_max_len) {
*needsCRC = 1;
return gotch;
}
len = get_utf16_char(str_i, str_i_max_len, *str_i_idx, u_ch,
&c);
/* These chars cannot be converted. Replace them. */
if (c == 0 || c > UNICODE_MAX || (conv_f && c > MAX_WCHAR_T) ||
(translate && c == '/')) {
illChar = 1;
if (!translate)
gotch = 1;
} else if (illChar)
break;
else
gotch = 1;
*str_i_idx += len;
}
if (illChar) {
*needsCRC = 1;
c = ILLEGAL_CHAR_MARK;
gotch = 1;
}
if (gotch) {
if (conv_f) {
len = conv_f(c, &str_o[*str_o_idx],
str_o_max_len - *str_o_idx);
} else {
len = utf32_to_utf8(c, &str_o[*str_o_idx],
str_o_max_len - *str_o_idx);
if (len < 0)
len = -ENAMETOOLONG;
}
/* Valid character? */
if (len >= 0)
*str_o_idx += len;
else if (len == -ENAMETOOLONG) {
*needsCRC = 1;
gotch = 0;
} else {
str_o[(*str_o_idx)++] = ILLEGAL_CHAR_MARK;
*needsCRC = 1;
}
}
return gotch;
}
static int udf_name_from_CS0(struct super_block *sb,
uint8_t *str_o, int str_max_len,
const uint8_t *ocu, int ocu_len,
int translate)
{
uint32_t c;
uint8_t cmp_id;
int idx, len;
int u_ch;
int needsCRC = 0;
int ext_i_len, ext_max_len;
int str_o_len = 0; /* Length of resulting output */
int ext_o_len = 0; /* Extension output length */
int ext_crc_len = 0; /* Extension output length if used with CRC */
int i_ext = -1; /* Extension position in input buffer */
int o_crc = 0; /* Rightmost possible output pos for CRC+ext */
unsigned short valueCRC;
uint8_t ext[EXT_SIZE * NLS_MAX_CHARSET_SIZE + 1];
uint8_t crc[CRC_LEN];
int (*conv_f)(wchar_t, unsigned char *, int);
if (str_max_len <= 0)
return 0;
if (ocu_len == 0) {
memset(str_o, 0, str_max_len);
return 0;
}
if (UDF_SB(sb)->s_nls_map)
conv_f = UDF_SB(sb)->s_nls_map->uni2char;
else
conv_f = NULL;
cmp_id = ocu[0];
if (cmp_id != 8 && cmp_id != 16) {
memset(str_o, 0, str_max_len);
pr_err("unknown compression code (%u)\n", cmp_id);
return -EINVAL;
}
u_ch = cmp_id >> 3;
ocu++;
ocu_len--;
if (ocu_len % u_ch) {
pr_err("incorrect filename length (%d)\n", ocu_len + 1);
return -EINVAL;
}
if (translate) {
/* Look for extension */
for (idx = ocu_len - u_ch, ext_i_len = 0;
(idx >= 0) && (ext_i_len < EXT_SIZE);
idx -= u_ch, ext_i_len++) {
c = ocu[idx];
if (u_ch > 1)
c = (c << 8) | ocu[idx + 1];
if (c == EXT_MARK) {
if (ext_i_len)
i_ext = idx;
break;
}
}
if (i_ext >= 0) {
/* Convert extension */
ext_max_len = min_t(int, sizeof(ext), str_max_len);
ext[ext_o_len++] = EXT_MARK;
idx = i_ext + u_ch;
while (udf_name_conv_char(ext, ext_max_len, &ext_o_len,
ocu, ocu_len, &idx,
u_ch, &needsCRC,
conv_f, translate)) {
if ((ext_o_len + CRC_LEN) < str_max_len)
ext_crc_len = ext_o_len;
}
}
}
idx = 0;
while (1) {
if (translate && (idx == i_ext)) {
if (str_o_len > (str_max_len - ext_o_len))
needsCRC = 1;
break;
}
if (!udf_name_conv_char(str_o, str_max_len, &str_o_len,
ocu, ocu_len, &idx,
u_ch, &needsCRC, conv_f, translate))
break;
if (translate &&
(str_o_len <= (str_max_len - ext_o_len - CRC_LEN)))
o_crc = str_o_len;
}
if (translate) {
if (str_o_len > 0 && str_o_len <= 2 && str_o[0] == '.' &&
(str_o_len == 1 || str_o[1] == '.'))
needsCRC = 1;
if (needsCRC) {
str_o_len = o_crc;
valueCRC = crc_itu_t(0, ocu, ocu_len);
crc[0] = CRC_MARK;
crc[1] = hex_asc_upper_hi(valueCRC >> 8);
crc[2] = hex_asc_upper_lo(valueCRC >> 8);
crc[3] = hex_asc_upper_hi(valueCRC);
crc[4] = hex_asc_upper_lo(valueCRC);
len = min_t(int, CRC_LEN, str_max_len - str_o_len);
memcpy(&str_o[str_o_len], crc, len);
str_o_len += len;
ext_o_len = ext_crc_len;
}
if (ext_o_len > 0) {
memcpy(&str_o[str_o_len], ext, ext_o_len);
str_o_len += ext_o_len;
}
}
return str_o_len;
}
static int udf_name_to_CS0(struct super_block *sb,
uint8_t *ocu, int ocu_max_len,
const uint8_t *str_i, int str_len)
{
int i, len;
unsigned int max_val;
int u_len, u_ch;
unicode_t uni_char;
int (*conv_f)(const unsigned char *, int, wchar_t *);
if (ocu_max_len <= 0)
return 0;
if (UDF_SB(sb)->s_nls_map)
conv_f = UDF_SB(sb)->s_nls_map->char2uni;
else
conv_f = NULL;
memset(ocu, 0, ocu_max_len);
ocu[0] = 8;
max_val = 0xff;
u_ch = 1;
try_again:
u_len = 1;
for (i = 0; i < str_len; i += len) {
/* Name didn't fit? */
if (u_len + u_ch > ocu_max_len)
return 0;
if (conv_f) {
wchar_t wchar;
len = conv_f(&str_i[i], str_len - i, &wchar);
if (len > 0)
uni_char = wchar;
} else {
len = utf8_to_utf32(&str_i[i], str_len - i,
&uni_char);
}
/* Invalid character, deal with it */
if (len <= 0 || uni_char > UNICODE_MAX) {
len = 1;
uni_char = '?';
}
if (uni_char > max_val) {
unicode_t c;
if (max_val == 0xff) {
max_val = 0xffff;
ocu[0] = 0x10;
u_ch = 2;
goto try_again;
}
/*
* Use UTF-16 encoding for chars outside we
* cannot encode directly.
*/
if (u_len + 2 * u_ch > ocu_max_len)
return 0;
uni_char -= PLANE_SIZE;
c = SURROGATE_PAIR |
((uni_char >> SURROGATE_CHAR_BITS) &
SURROGATE_CHAR_MASK);
ocu[u_len++] = (uint8_t)(c >> 8);
ocu[u_len++] = (uint8_t)(c & 0xff);
uni_char = SURROGATE_PAIR | SURROGATE_LOW |
(uni_char & SURROGATE_CHAR_MASK);
}
if (max_val == 0xffff)
ocu[u_len++] = (uint8_t)(uni_char >> 8);
ocu[u_len++] = (uint8_t)(uni_char & 0xff);
}
return u_len;
}
/*
* Convert CS0 dstring to output charset. Warning: This function may truncate
* input string if it is too long as it is used for informational strings only
* and it is better to truncate the string than to refuse mounting a media.
*/
int udf_dstrCS0toChar(struct super_block *sb, uint8_t *utf_o, int o_len,
const uint8_t *ocu_i, int i_len)
{
int s_len = 0;
if (i_len > 0) {
s_len = ocu_i[i_len - 1];
if (s_len >= i_len) {
pr_warn("incorrect dstring lengths (%d/%d),"
" truncating\n", s_len, i_len);
s_len = i_len - 1;
/* 2-byte encoding? Need to round properly... */
if (ocu_i[0] == 16)
s_len -= (s_len - 1) & 2;
}
}
return udf_name_from_CS0(sb, utf_o, o_len, ocu_i, s_len, 0);
}
int udf_get_filename(struct super_block *sb, const uint8_t *sname, int slen,
uint8_t *dname, int dlen)
{
int ret;
if (!slen)
return -EIO;
if (dlen <= 0)
return 0;
ret = udf_name_from_CS0(sb, dname, dlen, sname, slen, 1);
/* Zero length filename isn't valid... */
if (ret == 0)
ret = -EINVAL;
return ret;
}
int udf_put_filename(struct super_block *sb, const uint8_t *sname, int slen,
uint8_t *dname, int dlen)
{
return udf_name_to_CS0(sb, dname, dlen, sname, slen);
}