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
+41
View File
@@ -0,0 +1,41 @@
# SPDX-License-Identifier: GPL-2.0-only
config ISO9660_FS
tristate "ISO 9660 CDROM file system support"
select BUFFER_HEAD
help
This is the standard file system used on CD-ROMs. It was previously
known as "High Sierra File System" and is called "hsfs" on other
Unix systems. The so-called Rock-Ridge extensions which allow for
long Unix filenames and symbolic links are also supported by this
driver. If you have a CD-ROM drive and want to do more with it than
just listen to audio CDs and watch its LEDs, say Y (and read
<file:Documentation/filesystems/isofs.rst> and the CD-ROM-HOWTO,
available from <http://www.tldp.org/docs.html#howto>), thereby
enlarging your kernel by about 27 KB; otherwise say N.
To compile this file system support as a module, choose M here: the
module will be called isofs.
config JOLIET
bool "Microsoft Joliet CDROM extensions"
depends on ISO9660_FS
select NLS
help
Joliet is a Microsoft extension for the ISO 9660 CD-ROM file system
which allows for long filenames in unicode format (unicode is the
new 16 bit character code, successor to ASCII, which encodes the
characters of almost all languages of the world; see
<http://www.unicode.org/> for more information). Say Y here if you
want to be able to read Joliet CD-ROMs under Linux.
config ZISOFS
bool "Transparent decompression extension"
depends on ISO9660_FS
select ZLIB_INFLATE
help
This is a Linux-specific extension to RockRidge which lets you store
data in compressed form on a CD-ROM and have it transparently
decompressed when the CD-ROM is accessed. See
<http://www.kernel.org/pub/linux/utils/fs/zisofs/> for the tools
necessary to create such a filesystem. Say Y here if you want to be
able to read such compressed CD-ROMs.
+10
View File
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for the Linux isofs filesystem routines.
#
obj-$(CONFIG_ISO9660_FS) += isofs.o
isofs-y := namei.o inode.o dir.o util.o rock.o export.o
isofs-$(CONFIG_JOLIET) += joliet.o
isofs-$(CONFIG_ZISOFS) += compress.o
+383
View File
@@ -0,0 +1,383 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/* -*- linux-c -*- ------------------------------------------------------- *
*
* Copyright 2001 H. Peter Anvin - All Rights Reserved
*
* ----------------------------------------------------------------------- */
/*
* linux/fs/isofs/compress.c
*
* Transparent decompression of files on an iso9660 filesystem
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/bio.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/zlib.h>
#include "isofs.h"
#include "zisofs.h"
/* This should probably be global. */
static char zisofs_sink_page[PAGE_SIZE];
/*
* This contains the zlib memory allocation and the mutex for the
* allocation; this avoids failures at block-decompression time.
*/
static void *zisofs_zlib_workspace;
static DEFINE_MUTEX(zisofs_zlib_lock);
/*
* Read data of @inode from @block_start to @block_end and uncompress
* to one zisofs block. Store the data in the @pages array with @pcount
* entries. Start storing at offset @poffset of the first page.
*/
static loff_t zisofs_uncompress_block(struct inode *inode, loff_t block_start,
loff_t block_end, int pcount,
struct page **pages, unsigned poffset,
int *errp)
{
unsigned int zisofs_block_shift = ISOFS_I(inode)->i_format_parm[1];
unsigned int bufsize = ISOFS_BUFFER_SIZE(inode);
unsigned int bufshift = ISOFS_BUFFER_BITS(inode);
unsigned int bufmask = bufsize - 1;
int i, block_size = block_end - block_start;
z_stream stream = { .total_out = 0,
.avail_in = 0,
.avail_out = 0, };
int zerr;
int needblocks = (block_size + (block_start & bufmask) + bufmask)
>> bufshift;
int haveblocks;
blkcnt_t blocknum;
struct buffer_head **bhs;
int curbh, curpage;
if (block_size > deflateBound(1UL << zisofs_block_shift)) {
*errp = -EIO;
return 0;
}
/* Empty block? */
if (block_size == 0) {
for ( i = 0 ; i < pcount ; i++ ) {
if (!pages[i])
continue;
memzero_page(pages[i], 0, PAGE_SIZE);
SetPageUptodate(pages[i]);
}
return ((loff_t)pcount) << PAGE_SHIFT;
}
/* Because zlib is not thread-safe, do all the I/O at the top. */
blocknum = block_start >> bufshift;
bhs = kzalloc_objs(*bhs, needblocks + 1);
if (!bhs) {
*errp = -ENOMEM;
return 0;
}
haveblocks = isofs_get_blocks(inode, blocknum, bhs, needblocks);
bh_read_batch(haveblocks, bhs);
curbh = 0;
curpage = 0;
/*
* First block is special since it may be fractional. We also wait for
* it before grabbing the zlib mutex; odds are that the subsequent
* blocks are going to come in in short order so we don't hold the zlib
* mutex longer than necessary.
*/
if (!bhs[0])
goto b_eio;
wait_on_buffer(bhs[0]);
if (!buffer_uptodate(bhs[0])) {
*errp = -EIO;
goto b_eio;
}
stream.workspace = zisofs_zlib_workspace;
mutex_lock(&zisofs_zlib_lock);
zerr = zlib_inflateInit(&stream);
if (zerr != Z_OK) {
if (zerr == Z_MEM_ERROR)
*errp = -ENOMEM;
else
*errp = -EIO;
printk(KERN_DEBUG "zisofs: zisofs_inflateInit returned %d\n",
zerr);
goto z_eio;
}
while (curpage < pcount && curbh < haveblocks &&
zerr != Z_STREAM_END) {
if (!stream.avail_out) {
if (pages[curpage]) {
stream.next_out = kmap_local_page(pages[curpage])
+ poffset;
stream.avail_out = PAGE_SIZE - poffset;
poffset = 0;
} else {
stream.next_out = (void *)&zisofs_sink_page;
stream.avail_out = PAGE_SIZE;
}
}
if (!stream.avail_in) {
wait_on_buffer(bhs[curbh]);
if (!buffer_uptodate(bhs[curbh])) {
*errp = -EIO;
break;
}
stream.next_in = bhs[curbh]->b_data +
(block_start & bufmask);
stream.avail_in = min_t(unsigned, bufsize -
(block_start & bufmask),
block_size);
block_size -= stream.avail_in;
block_start = 0;
}
while (stream.avail_out && stream.avail_in) {
zerr = zlib_inflate(&stream, Z_SYNC_FLUSH);
if (zerr == Z_BUF_ERROR && stream.avail_in == 0)
break;
if (zerr == Z_STREAM_END)
break;
if (zerr != Z_OK) {
/* EOF, error, or trying to read beyond end of input */
if (zerr == Z_MEM_ERROR)
*errp = -ENOMEM;
else {
printk(KERN_DEBUG
"zisofs: zisofs_inflate returned"
" %d, inode = %llu,"
" page idx = %d, bh idx = %d,"
" avail_in = %ld,"
" avail_out = %ld\n",
zerr, inode->i_ino, curpage,
curbh, stream.avail_in,
stream.avail_out);
*errp = -EIO;
}
goto inflate_out;
}
}
if (!stream.avail_out) {
/* This page completed */
if (pages[curpage]) {
flush_dcache_page(pages[curpage]);
SetPageUptodate(pages[curpage]);
}
if (stream.next_out != (unsigned char *)zisofs_sink_page) {
kunmap_local(stream.next_out);
stream.next_out = NULL;
}
curpage++;
}
if (!stream.avail_in)
curbh++;
}
inflate_out:
zlib_inflateEnd(&stream);
if (stream.next_out && stream.next_out != (unsigned char *)zisofs_sink_page)
kunmap_local(stream.next_out);
z_eio:
mutex_unlock(&zisofs_zlib_lock);
b_eio:
for (i = 0; i < haveblocks; i++)
brelse(bhs[i]);
kfree(bhs);
return stream.total_out;
}
/*
* Uncompress data so that pages[full_page] is fully uptodate and possibly
* fills in other pages if we have data for them.
*/
static int zisofs_fill_pages(struct inode *inode, int full_page, int pcount,
struct page **pages)
{
loff_t start_off, end_off;
loff_t block_start, block_end;
unsigned int header_size = ISOFS_I(inode)->i_format_parm[0];
unsigned int zisofs_block_shift = ISOFS_I(inode)->i_format_parm[1];
unsigned int blockptr;
loff_t poffset = 0;
blkcnt_t cstart_block, cend_block;
struct buffer_head *bh;
unsigned int blkbits = ISOFS_BUFFER_BITS(inode);
unsigned int blksize = 1 << blkbits;
int err;
loff_t ret;
BUG_ON(!pages[full_page]);
/*
* We want to read at least 'full_page' page. Because we have to
* uncompress the whole compression block anyway, fill the surrounding
* pages with the data we have anyway...
*/
start_off = page_offset(pages[full_page]);
end_off = min_t(loff_t, start_off + PAGE_SIZE, inode->i_size);
cstart_block = start_off >> zisofs_block_shift;
cend_block = (end_off + (1 << zisofs_block_shift) - 1)
>> zisofs_block_shift;
WARN_ON(start_off - (full_page << PAGE_SHIFT) !=
((cstart_block << zisofs_block_shift) & PAGE_MASK));
/* Find the pointer to this specific chunk */
/* Note: we're not using isonum_731() here because the data is known aligned */
/* Note: header_size is in 32-bit words (4 bytes) */
blockptr = (header_size + cstart_block) << 2;
bh = isofs_bread(inode, blockptr >> blkbits);
if (!bh)
return -EIO;
block_start = le32_to_cpu(*(__le32 *)
(bh->b_data + (blockptr & (blksize - 1))));
while (cstart_block < cend_block && pcount > 0) {
/* Load end of the compressed block in the file */
blockptr += 4;
/* Traversed to next block? */
if (!(blockptr & (blksize - 1))) {
brelse(bh);
bh = isofs_bread(inode, blockptr >> blkbits);
if (!bh)
return -EIO;
}
block_end = le32_to_cpu(*(__le32 *)
(bh->b_data + (blockptr & (blksize - 1))));
if (block_start > block_end) {
brelse(bh);
return -EIO;
}
err = 0;
ret = zisofs_uncompress_block(inode, block_start, block_end,
pcount, pages, poffset, &err);
poffset += ret;
pages += poffset >> PAGE_SHIFT;
pcount -= poffset >> PAGE_SHIFT;
full_page -= poffset >> PAGE_SHIFT;
poffset &= ~PAGE_MASK;
if (err) {
brelse(bh);
/*
* Did we finish reading the page we really wanted
* to read?
*/
if (full_page < 0)
return 0;
return err;
}
block_start = block_end;
cstart_block++;
}
if (poffset && *pages) {
memzero_page(*pages, poffset, PAGE_SIZE - poffset);
SetPageUptodate(*pages);
}
return 0;
}
/*
* When decompressing, we typically obtain more than one page
* per reference. We inject the additional pages into the page
* cache as a form of readahead.
*/
static int zisofs_read_folio(struct file *file, struct folio *folio)
{
struct inode *inode = file_inode(file);
struct address_space *mapping = inode->i_mapping;
int err;
int i, pcount, full_page;
unsigned int zisofs_block_shift = ISOFS_I(inode)->i_format_parm[1];
unsigned int zisofs_pages_per_cblock =
PAGE_SHIFT <= zisofs_block_shift ?
(1 << (zisofs_block_shift - PAGE_SHIFT)) : 0;
struct page **pages;
pgoff_t index = folio->index, end_index;
end_index = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
/*
* If this folio is wholly outside i_size we just return zero;
* do_generic_file_read() will handle this for us
*/
if (index >= end_index) {
folio_end_read(folio, true);
return 0;
}
if (PAGE_SHIFT <= zisofs_block_shift) {
/* We have already been given one page, this is the one
we must do. */
full_page = index & (zisofs_pages_per_cblock - 1);
pcount = min_t(int, zisofs_pages_per_cblock,
end_index - (index & ~(zisofs_pages_per_cblock - 1)));
index -= full_page;
} else {
full_page = 0;
pcount = 1;
}
pages = kzalloc_objs(*pages,
max_t(unsigned int, zisofs_pages_per_cblock, 1));
if (!pages) {
folio_unlock(folio);
return -ENOMEM;
}
pages[full_page] = &folio->page;
for (i = 0; i < pcount; i++, index++) {
if (i != full_page)
pages[i] = grab_cache_page_nowait(mapping, index);
}
err = zisofs_fill_pages(inode, full_page, pcount, pages);
/* Release any residual pages, do not SetPageUptodate */
for (i = 0; i < pcount; i++) {
if (pages[i]) {
flush_dcache_page(pages[i]);
unlock_page(pages[i]);
if (i != full_page)
put_page(pages[i]);
}
}
/* At this point, err contains 0 or -EIO depending on the "critical" page */
kfree(pages);
return err;
}
const struct address_space_operations zisofs_aops = {
.read_folio = zisofs_read_folio,
/* No bmap operation supported */
};
int __init zisofs_init(void)
{
zisofs_zlib_workspace = vmalloc(zlib_inflate_workspacesize());
if ( !zisofs_zlib_workspace )
return -ENOMEM;
return 0;
}
void zisofs_cleanup(void)
{
vfree(zisofs_zlib_workspace);
}
+303
View File
@@ -0,0 +1,303 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/fs/isofs/dir.c
*
* (C) 1992, 1993, 1994 Eric Youngdale Modified for ISO 9660 filesystem.
*
* (C) 1991 Linus Torvalds - minix filesystem
*
* Steve Beynon : Missing last directory entries fixed
* (stephen@askone.demon.co.uk) : 21st June 1996
*
* isofs directory handling functions
*/
#include <linux/gfp.h>
#include <linux/filelock.h>
#include <linux/slab.h>
#include "isofs.h"
#include <linux/fileattr.h>
int isofs_name_translate(struct iso_directory_record *de, char *new, struct inode *inode)
{
char * old = de->name;
int len = de->name_len[0];
int i;
for (i = 0; i < len; i++) {
unsigned char c = old[i];
if (!c)
break;
if (c >= 'A' && c <= 'Z')
c |= 0x20; /* lower case */
/* Drop trailing '.;1' (ISO 9660:1988 7.5.1 requires period) */
if (c == '.' && i == len - 3 && old[i + 1] == ';' && old[i + 2] == '1')
break;
/* Drop trailing ';1' */
if (c == ';' && i == len - 2 && old[i + 1] == '1')
break;
/* Convert remaining ';' to '.' */
/* Also '/' to '.' (broken Acorn-generated ISO9660 images) */
if (c == ';' || c == '/')
c = '.';
new[i] = c;
}
return i;
}
/* Acorn extensions written by Matthew Wilcox <willy@infradead.org> 1998 */
int get_acorn_filename(struct iso_directory_record *de,
char *retname, struct inode *inode)
{
int std;
unsigned char *chr;
int retnamlen = isofs_name_translate(de, retname, inode);
if (retnamlen == 0)
return 0;
std = sizeof(struct iso_directory_record) + de->name_len[0];
if (std & 1)
std++;
if (de->length[0] - std != 32)
return retnamlen;
chr = ((unsigned char *) de) + std;
if (strncmp(chr, "ARCHIMEDES", 10))
return retnamlen;
if ((*retname == '_') && ((chr[19] & 1) == 1))
*retname = '!';
if (((de->flags[0] & 2) == 0) && (chr[13] == 0xff)
&& ((chr[12] & 0xf0) == 0xf0)) {
retname[retnamlen] = ',';
sprintf(retname+retnamlen+1, "%3.3x",
((chr[12] & 0xf) << 8) | chr[11]);
retnamlen += 4;
}
return retnamlen;
}
/*
* This should _really_ be cleaned up some day..
*/
static int do_isofs_readdir(struct inode *inode, struct file *file,
struct dir_context *ctx,
char *tmpname, struct iso_directory_record *tmpde)
{
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
unsigned char bufbits = ISOFS_BUFFER_BITS(inode);
unsigned long block, offset, block_saved, offset_saved;
unsigned long inode_number = 0; /* Quiet GCC */
struct buffer_head *bh = NULL;
int len;
int map;
int first_de = 1;
char *p = NULL; /* Quiet GCC */
struct iso_directory_record *de;
struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb);
offset = ctx->pos & (bufsize - 1);
block = ctx->pos >> bufbits;
while (ctx->pos < inode->i_size) {
int de_len;
if (!bh) {
bh = isofs_bread(inode, block);
if (!bh)
return 0;
}
de = (struct iso_directory_record *) (bh->b_data + offset);
de_len = *(unsigned char *)de;
/*
* If the length byte is zero, we should move on to the next
* CDROM sector. If we are at the end of the directory, we
* kick out of the while loop.
*/
if (de_len == 0) {
brelse(bh);
bh = NULL;
ctx->pos = (ctx->pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
block = ctx->pos >> bufbits;
offset = 0;
continue;
}
block_saved = block;
offset_saved = offset;
offset += de_len;
/* Make sure we have a full directory entry */
if (offset >= bufsize) {
int slop = bufsize - offset + de_len;
memcpy(tmpde, de, slop);
offset &= bufsize - 1;
block++;
brelse(bh);
bh = NULL;
if (offset) {
bh = isofs_bread(inode, block);
if (!bh)
return 0;
memcpy((void *) tmpde + slop, bh->b_data, offset);
}
de = tmpde;
}
/* Basic sanity check, whether name doesn't exceed dir entry */
if (de_len < sizeof(struct iso_directory_record) ||
de_len < de->name_len[0] +
sizeof(struct iso_directory_record)) {
printk(KERN_NOTICE "iso9660: Corrupted directory entry"
" in block %lu of inode %llu\n", block,
inode->i_ino);
brelse(bh);
return -EIO;
}
if (first_de) {
isofs_normalize_block_and_offset(de,
&block_saved,
&offset_saved);
inode_number = isofs_get_ino(block_saved,
offset_saved, bufbits);
}
if (de->flags[-sbi->s_high_sierra] & 0x80) {
first_de = 0;
ctx->pos += de_len;
continue;
}
first_de = 1;
/* Handle the case of the '.' directory */
if (de->name_len[0] == 1 && de->name[0] == 0) {
if (!dir_emit_dot(file, ctx))
break;
ctx->pos += de_len;
continue;
}
len = 0;
/* Handle the case of the '..' directory */
if (de->name_len[0] == 1 && de->name[0] == 1) {
if (!dir_emit_dotdot(file, ctx))
break;
ctx->pos += de_len;
continue;
}
/* Handle everything else. Do name translation if there
is no Rock Ridge NM field. */
/*
* Do not report hidden files if so instructed, or associated
* files unless instructed to do so
*/
if ((sbi->s_hide && (de->flags[-sbi->s_high_sierra] & 1)) ||
(!sbi->s_showassoc &&
(de->flags[-sbi->s_high_sierra] & 4))) {
ctx->pos += de_len;
continue;
}
map = 1;
if (sbi->s_rock) {
len = get_rock_ridge_filename(de, tmpname, inode);
if (len != 0) { /* may be -1 */
p = tmpname;
map = 0;
}
}
if (map) {
#ifdef CONFIG_JOLIET
if (sbi->s_joliet_level) {
len = get_joliet_filename(de, tmpname, inode);
p = tmpname;
} else
#endif
if (sbi->s_mapping == 'a') {
len = get_acorn_filename(de, tmpname, inode);
p = tmpname;
} else
if (sbi->s_mapping == 'n') {
len = isofs_name_translate(de, tmpname, inode);
p = tmpname;
} else {
p = de->name;
len = de->name_len[0];
}
}
if (len > 0) {
if (!dir_emit(ctx, p, len, inode_number, DT_UNKNOWN))
break;
}
ctx->pos += de_len;
}
if (bh)
brelse(bh);
return 0;
}
/*
* Handle allocation of temporary space for name translation and
* handling split directory entries.. The real work is done by
* "do_isofs_readdir()".
*/
static int isofs_readdir(struct file *file, struct dir_context *ctx)
{
int result;
char *tmpname;
struct iso_directory_record *tmpde;
struct inode *inode = file_inode(file);
tmpname = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (tmpname == NULL)
return -ENOMEM;
tmpde = (struct iso_directory_record *) (tmpname+1024);
result = do_isofs_readdir(inode, file, ctx, tmpname, tmpde);
kfree(tmpname);
return result;
}
int isofs_fileattr_get(struct dentry *dentry, struct file_kattr *fa)
{
struct isofs_sb_info *sbi = ISOFS_SB(dentry->d_sb);
if (sbi->s_check == 'r') {
fa->fsx_xflags |= FS_XFLAG_CASEFOLD;
fa->flags |= FS_CASEFOLD_FL;
}
if (!sbi->s_joliet_level && !sbi->s_rock &&
(sbi->s_mapping == 'n' || sbi->s_mapping == 'a'))
fa->fsx_xflags |= FS_XFLAG_CASENONPRESERVING;
return 0;
}
const struct file_operations isofs_dir_operations =
{
.llseek = generic_file_llseek,
.read = generic_read_dir,
.iterate_shared = isofs_readdir,
.setlease = generic_setlease,
};
/*
* directories can handle most operations...
*/
const struct inode_operations isofs_dir_inode_operations =
{
.lookup = isofs_lookup,
.fileattr_get = isofs_fileattr_get,
};
+193
View File
@@ -0,0 +1,193 @@
// SPDX-License-Identifier: GPL-2.0
/*
* fs/isofs/export.c
*
* (C) 2004 Paul Serice - The new inode scheme requires switching
* from iget() to iget5_locked() which means
* the NFS export operations have to be hand
* coded because the default routines rely on
* iget().
*
* The following files are helpful:
*
* Documentation/filesystems/nfs/exporting.rst
* fs/exportfs/expfs.c.
*/
#include "isofs.h"
static struct dentry *
isofs_export_iget(struct super_block *sb,
unsigned long block,
unsigned long offset,
__u32 generation)
{
struct inode *inode;
if (block == 0 || block >= ISOFS_SB(sb)->s_nzones)
return ERR_PTR(-ESTALE);
inode = isofs_iget(sb, block, offset);
if (IS_ERR(inode))
return ERR_CAST(inode);
if (generation && inode->i_generation != generation) {
iput(inode);
return ERR_PTR(-ESTALE);
}
return d_obtain_alias(inode);
}
/* This function is surprisingly simple. The trick is understanding
* that "child" is always a directory. So, to find its parent, you
* simply need to find its ".." entry, normalize its block and offset,
* and return the underlying inode. See the comments for
* isofs_normalize_block_and_offset(). */
static struct dentry *isofs_export_get_parent(struct dentry *child)
{
unsigned long parent_block = 0;
unsigned long parent_offset = 0;
struct inode *child_inode = d_inode(child);
struct iso_inode_info *e_child_inode = ISOFS_I(child_inode);
struct iso_directory_record *de = NULL;
struct buffer_head * bh = NULL;
struct dentry *rv = NULL;
/* "child" must always be a directory. */
if (!S_ISDIR(child_inode->i_mode)) {
printk(KERN_ERR "isofs: isofs_export_get_parent(): "
"child is not a directory!\n");
rv = ERR_PTR(-EACCES);
goto out;
}
/* It is an invariant that the directory offset is zero. If
* it is not zero, it means the directory failed to be
* normalized for some reason. */
if (e_child_inode->i_iget5_offset != 0) {
printk(KERN_ERR "isofs: isofs_export_get_parent(): "
"child directory not normalized!\n");
rv = ERR_PTR(-EACCES);
goto out;
}
/* The child inode has been normalized such that its
* i_iget5_block value points to the "." entry. Fortunately,
* the ".." entry is located in the same block. */
parent_block = e_child_inode->i_iget5_block;
/* Get the block in question. */
bh = sb_bread(child_inode->i_sb, parent_block);
if (bh == NULL) {
rv = ERR_PTR(-EACCES);
goto out;
}
/* This is the "." entry. */
de = (struct iso_directory_record*)bh->b_data;
/* The ".." entry is always the second entry. */
parent_offset = (unsigned long)isonum_711(de->length);
de = (struct iso_directory_record*)(bh->b_data + parent_offset);
/* Verify it is in fact the ".." entry. */
if ((isonum_711(de->name_len) != 1) || (de->name[0] != 1)) {
printk(KERN_ERR "isofs: Unable to find the \"..\" "
"directory for NFS.\n");
rv = ERR_PTR(-EACCES);
goto out;
}
/* Normalize */
isofs_normalize_block_and_offset(de, &parent_block, &parent_offset);
rv = d_obtain_alias(isofs_iget(child_inode->i_sb, parent_block,
parent_offset));
out:
if (bh)
brelse(bh);
return rv;
}
static int
isofs_export_encode_fh(struct inode *inode,
__u32 *fh32,
int *max_len,
struct inode *parent)
{
struct iso_inode_info * ei = ISOFS_I(inode);
int len = *max_len;
int type = 1;
__u16 *fh16 = (__u16*)fh32;
/*
* WARNING: max_len is 5 for NFSv2. Because of this
* limitation, we use the lower 16 bits of fh32[1] to hold the
* offset of the inode and the upper 16 bits of fh32[1] to
* hold the offset of the parent.
*/
if (parent && (len < 5)) {
*max_len = 5;
return FILEID_INVALID;
} else if (len < 3) {
*max_len = 3;
return FILEID_INVALID;
}
len = 3;
fh32[0] = ei->i_iget5_block;
fh16[2] = (__u16)ei->i_iget5_offset; /* fh16 [sic] */
fh16[3] = 0; /* avoid leaking uninitialized data */
fh32[2] = inode->i_generation;
if (parent) {
struct iso_inode_info *eparent;
eparent = ISOFS_I(parent);
fh32[3] = eparent->i_iget5_block;
fh16[3] = (__u16)eparent->i_iget5_offset; /* fh16 [sic] */
fh32[4] = parent->i_generation;
len = 5;
type = 2;
}
*max_len = len;
return type;
}
struct isofs_fid {
u32 block;
u16 offset;
u16 parent_offset;
u32 generation;
u32 parent_block;
u32 parent_generation;
};
static struct dentry *isofs_fh_to_dentry(struct super_block *sb,
struct fid *fid, int fh_len, int fh_type)
{
struct isofs_fid *ifid = (struct isofs_fid *)fid;
if (fh_len < 3 || fh_type > 2)
return NULL;
return isofs_export_iget(sb, ifid->block, ifid->offset,
ifid->generation);
}
static struct dentry *isofs_fh_to_parent(struct super_block *sb,
struct fid *fid, int fh_len, int fh_type)
{
struct isofs_fid *ifid = (struct isofs_fid *)fid;
if (fh_len < 2 || fh_type != 2)
return NULL;
return isofs_export_iget(sb,
fh_len > 3 ? ifid->parent_block : 0,
ifid->parent_offset,
fh_len > 4 ? ifid->parent_generation : 0);
}
const struct export_operations isofs_export_ops = {
.encode_fh = isofs_export_encode_fh,
.fh_to_dentry = isofs_fh_to_dentry,
.fh_to_parent = isofs_fh_to_parent,
.get_parent = isofs_export_get_parent,
};
File diff suppressed because it is too large Load Diff
+206
View File
@@ -0,0 +1,206 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/fs.h>
#include <linux/buffer_head.h>
#include <linux/exportfs.h>
#include <linux/iso_fs.h>
#include <linux/unaligned.h>
enum isofs_file_format {
isofs_file_normal = 0,
isofs_file_sparse = 1,
isofs_file_compressed = 2,
};
/*
* iso fs inode data in memory
*/
struct iso_inode_info {
unsigned long i_iget5_block;
unsigned long i_iget5_offset;
unsigned int i_first_extent;
unsigned char i_file_format;
unsigned char i_format_parm[3];
unsigned long i_next_section_block;
unsigned long i_next_section_offset;
off_t i_section_size;
struct inode vfs_inode;
};
/*
* iso9660 super-block data in memory
*/
struct isofs_sb_info {
unsigned long s_ninodes;
unsigned long s_nzones;
unsigned long s_firstdatazone;
unsigned long s_log_zone_size;
unsigned long s_max_size;
int s_rock_offset; /* offset of SUSP fields within SU area */
s32 s_sbsector;
unsigned char s_joliet_level;
unsigned char s_mapping;
unsigned char s_check;
unsigned char s_session;
unsigned int s_high_sierra:1;
unsigned int s_rock:2;
unsigned int s_cruft:1; /* Broken disks with high byte of length
* containing junk */
unsigned int s_nocompress:1;
unsigned int s_hide:1;
unsigned int s_showassoc:1;
unsigned int s_overriderockperm:1;
unsigned int s_uid_set:1;
unsigned int s_gid_set:1;
umode_t s_fmode;
umode_t s_dmode;
kgid_t s_gid;
kuid_t s_uid;
struct nls_table *s_nls_iocharset; /* Native language support table */
};
#define ISOFS_INVALID_MODE ((umode_t) -1)
static inline struct isofs_sb_info *ISOFS_SB(struct super_block *sb)
{
return sb->s_fs_info;
}
static inline struct iso_inode_info *ISOFS_I(struct inode *inode)
{
return container_of(inode, struct iso_inode_info, vfs_inode);
}
static inline int isonum_711(u8 *p)
{
return *p;
}
static inline int isonum_712(s8 *p)
{
return *p;
}
static inline unsigned int isonum_721(u8 *p)
{
return get_unaligned_le16(p);
}
static inline unsigned int isonum_722(u8 *p)
{
return get_unaligned_be16(p);
}
static inline unsigned int isonum_723(u8 *p)
{
/* Ignore bigendian datum due to broken mastering programs */
return get_unaligned_le16(p);
}
static inline unsigned int isonum_731(u8 *p)
{
return get_unaligned_le32(p);
}
static inline unsigned int isonum_732(u8 *p)
{
return get_unaligned_be32(p);
}
static inline unsigned int isonum_733(u8 *p)
{
/* Ignore bigendian datum due to broken mastering programs */
return get_unaligned_le32(p);
}
#define ISO_DATE_HIGH_SIERRA (1 << 0)
#define ISO_DATE_LONG_FORM (1 << 1)
struct timespec64 iso_date(u8 *p, int flags);
struct inode; /* To make gcc happy */
extern int parse_rock_ridge_inode(struct iso_directory_record *, struct inode *, int relocated);
extern int get_rock_ridge_filename(struct iso_directory_record *, char *, struct inode *);
extern int isofs_name_translate(struct iso_directory_record *, char *, struct inode *);
int get_joliet_filename(struct iso_directory_record *, unsigned char *, struct inode *);
int get_acorn_filename(struct iso_directory_record *, char *, struct inode *);
extern struct dentry *isofs_lookup(struct inode *, struct dentry *, unsigned int flags);
extern struct buffer_head *isofs_bread(struct inode *, sector_t);
extern int isofs_get_blocks(struct inode *, sector_t, struct buffer_head **, unsigned long);
struct inode *__isofs_iget(struct super_block *sb,
unsigned long block,
unsigned long offset,
int relocated);
static inline struct inode *isofs_iget(struct super_block *sb,
unsigned long block,
unsigned long offset)
{
return __isofs_iget(sb, block, offset, 0);
}
static inline struct inode *isofs_iget_reloc(struct super_block *sb,
unsigned long block,
unsigned long offset)
{
return __isofs_iget(sb, block, offset, 1);
}
/* Because the inode number is no longer relevant to finding the
* underlying meta-data for an inode, we are free to choose a more
* convenient 32-bit number as the inode number. The inode numbering
* scheme was recommended by Sergey Vlasov and Eric Lammerts. */
static inline unsigned long isofs_get_ino(unsigned long block,
unsigned long offset,
unsigned long bufbits)
{
return (block << (bufbits - 5)) | (offset >> 5);
}
/* Every directory can have many redundant directory entries scattered
* throughout the directory tree. First there is the directory entry
* with the name of the directory stored in the parent directory.
* Then, there is the "." directory entry stored in the directory
* itself. Finally, there are possibly many ".." directory entries
* stored in all the subdirectories.
*
* In order for the NFS get_parent() method to work and for the
* general consistency of the dcache, we need to make sure the
* "i_iget5_block" and "i_iget5_offset" all point to exactly one of
* the many redundant entries for each directory. We normalize the
* block and offset by always making them point to the "." directory.
*
* Notice that we do not use the entry for the directory with the name
* that is located in the parent directory. Even though choosing this
* first directory is more natural, it is much easier to find the "."
* entry in the NFS get_parent() method because it is implicitly
* encoded in the "extent + ext_attr_length" fields of _all_ the
* redundant entries for the directory. Thus, it can always be
* reached regardless of which directory entry you have in hand.
*
* This works because the "." entry is simply the first directory
* record when you start reading the file that holds all the directory
* records, and this file starts at "extent + ext_attr_length" blocks.
* Because the "." entry is always the first entry listed in the
* directories file, the normalized "offset" value is always 0.
*
* You should pass the directory entry in "de". On return, "block"
* and "offset" will hold normalized values. Only directories are
* affected making it safe to call even for non-directory file
* types. */
static inline void
isofs_normalize_block_and_offset(struct iso_directory_record* de,
unsigned long *block,
unsigned long *offset)
{
/* Only directories are normalized. */
if (de->flags[0] & 2) {
*offset = 0;
*block = (unsigned long)isonum_733(de->extent)
+ (unsigned long)isonum_711(de->ext_attr_length);
}
}
struct file_kattr;
int isofs_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
extern const struct inode_operations isofs_dir_inode_operations;
extern const struct file_operations isofs_dir_operations;
extern const struct address_space_operations isofs_symlink_aops;
extern const struct export_operations isofs_export_ops;
+68
View File
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/fs/isofs/joliet.c
*
* (C) 1996 Gordon Chaffee
*
* Joliet: Microsoft's Unicode extensions to iso9660
*/
#include <linux/types.h>
#include <linux/nls.h>
#include "isofs.h"
/*
* Convert Unicode 16 to UTF-8 or ASCII.
*/
static int
uni16_to_x8(unsigned char *ascii, __be16 *uni, int len, struct nls_table *nls)
{
__be16 *ip, ch;
unsigned char *op;
ip = uni;
op = ascii;
while ((ch = get_unaligned(ip)) && len) {
int llen;
llen = nls->uni2char(be16_to_cpu(ch), op, NLS_MAX_CHARSET_SIZE);
if (llen > 0)
op += llen;
else
*op++ = '?';
ip++;
len--;
}
*op = 0;
return (op - ascii);
}
int
get_joliet_filename(struct iso_directory_record * de, unsigned char *outname, struct inode * inode)
{
struct nls_table *nls;
unsigned char len = 0;
nls = ISOFS_SB(inode->i_sb)->s_nls_iocharset;
if (!nls) {
len = utf16s_to_utf8s((const wchar_t *) de->name,
de->name_len[0] >> 1, UTF16_BIG_ENDIAN,
outname, PAGE_SIZE);
} else {
len = uni16_to_x8(outname, (__be16 *) de->name,
de->name_len[0] >> 1, nls);
}
if ((len > 2) && (outname[len-2] == ';') && (outname[len-1] == '1'))
len -= 2;
/*
* Windows doesn't like periods at the end of a name,
* so neither do we
*/
while (len >= 2 && (outname[len-1] == '.'))
len--;
return len;
}
+167
View File
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/fs/isofs/namei.c
*
* (C) 1992 Eric Youngdale Modified for ISO 9660 filesystem.
*
* (C) 1991 Linus Torvalds - minix filesystem
*/
#include <linux/gfp.h>
#include "isofs.h"
static int
isofs_cmp(struct dentry *dentry, const char *compare, int dlen)
{
if (likely(!dentry->d_op))
return dentry->d_name.len != dlen || memcmp(dentry->d_name.name, compare, dlen);
return dentry->d_op->d_compare(NULL, dentry->d_name.len, dentry->d_name.name,
&QSTR_LEN(compare, dlen));
}
/*
* isofs_find_entry()
*
* finds an entry in the specified directory with the wanted name. It
* returns the inode number of the found entry, or 0 on error.
*/
static unsigned long
isofs_find_entry(struct inode *dir, struct dentry *dentry,
unsigned long *block_rv, unsigned long *offset_rv,
char *tmpname, struct iso_directory_record *tmpde)
{
unsigned long bufsize = ISOFS_BUFFER_SIZE(dir);
unsigned char bufbits = ISOFS_BUFFER_BITS(dir);
unsigned long block, f_pos, offset, block_saved, offset_saved;
struct buffer_head *bh = NULL;
struct isofs_sb_info *sbi = ISOFS_SB(dir->i_sb);
if (!ISOFS_I(dir)->i_first_extent)
return 0;
f_pos = 0;
offset = 0;
block = 0;
while (f_pos < dir->i_size) {
struct iso_directory_record *de;
int de_len, match, i, dlen;
char *dpnt;
if (!bh) {
bh = isofs_bread(dir, block);
if (!bh)
return 0;
}
de = (struct iso_directory_record *) (bh->b_data + offset);
de_len = *(unsigned char *) de;
if (!de_len) {
brelse(bh);
bh = NULL;
f_pos = (f_pos + ISOFS_BLOCK_SIZE) & ~(ISOFS_BLOCK_SIZE - 1);
block = f_pos >> bufbits;
offset = 0;
continue;
}
block_saved = bh->b_blocknr;
offset_saved = offset;
offset += de_len;
f_pos += de_len;
/* Make sure we have a full directory entry */
if (offset >= bufsize) {
int slop = bufsize - offset + de_len;
memcpy(tmpde, de, slop);
offset &= bufsize - 1;
block++;
brelse(bh);
bh = NULL;
if (offset) {
bh = isofs_bread(dir, block);
if (!bh)
return 0;
memcpy((void *) tmpde + slop, bh->b_data, offset);
}
de = tmpde;
}
dlen = de->name_len[0];
dpnt = de->name;
/* Basic sanity check, whether name doesn't exceed dir entry */
if (de_len < dlen + sizeof(struct iso_directory_record)) {
printk(KERN_NOTICE "iso9660: Corrupted directory entry"
" in block %lu of inode %llu\n", block,
dir->i_ino);
brelse(bh);
return 0;
}
if (sbi->s_rock &&
((i = get_rock_ridge_filename(de, tmpname, dir)))) {
dlen = i; /* possibly -1 */
dpnt = tmpname;
#ifdef CONFIG_JOLIET
} else if (sbi->s_joliet_level) {
dlen = get_joliet_filename(de, tmpname, dir);
dpnt = tmpname;
#endif
} else if (sbi->s_mapping == 'a') {
dlen = get_acorn_filename(de, tmpname, dir);
dpnt = tmpname;
} else if (sbi->s_mapping == 'n') {
dlen = isofs_name_translate(de, tmpname, dir);
dpnt = tmpname;
}
/*
* Skip hidden or associated files unless hide or showassoc,
* respectively, is set
*/
match = 0;
if (dlen > 0 &&
(!sbi->s_hide ||
(!(de->flags[-sbi->s_high_sierra] & 1))) &&
(sbi->s_showassoc ||
(!(de->flags[-sbi->s_high_sierra] & 4)))) {
if (dpnt && (dlen > 1 || dpnt[0] > 1))
match = (isofs_cmp(dentry, dpnt, dlen) == 0);
}
if (match) {
isofs_normalize_block_and_offset(de,
&block_saved,
&offset_saved);
*block_rv = block_saved;
*offset_rv = offset_saved;
brelse(bh);
return 1;
}
}
brelse(bh);
return 0;
}
struct dentry *isofs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
{
int found;
unsigned long block;
unsigned long offset;
struct inode *inode;
struct page *page;
page = alloc_page(GFP_USER);
if (!page)
return ERR_PTR(-ENOMEM);
found = isofs_find_entry(dir, dentry,
&block, &offset,
page_address(page),
1024 + page_address(page));
__free_page(page);
inode = found ? isofs_iget(dir->i_sb, block, offset) : NULL;
return d_splice_alias(inode, dentry);
}
+830
View File
@@ -0,0 +1,830 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/fs/isofs/rock.c
*
* (C) 1992, 1993 Eric Youngdale
*
* Rock Ridge Extensions to iso9660
*/
#include <linux/slab.h>
#include <linux/pagemap.h>
#include "isofs.h"
#include "rock.h"
/*
* These functions are designed to read the system areas of a directory record
* and extract relevant information. There are different functions provided
* depending upon what information we need at the time. One function fills
* out an inode structure, a second one extracts a filename, a third one
* returns a symbolic link name, and a fourth one returns the extent number
* for the file.
*/
#define SIG(A,B) ((A) | ((B) << 8)) /* isonum_721() */
struct rock_state {
void *buffer;
unsigned char *chr;
int len;
int cont_size;
int cont_extent;
int cont_offset;
int cont_loops;
struct inode *inode;
};
/*
* This is a way of ensuring that we have something in the system
* use fields that is compatible with Rock Ridge. Return zero on success.
*/
static int check_sp(struct rock_ridge *rr, struct inode *inode)
{
if (rr->u.SP.magic[0] != 0xbe)
return -1;
if (rr->u.SP.magic[1] != 0xef)
return -1;
ISOFS_SB(inode->i_sb)->s_rock_offset = rr->u.SP.skip;
return 0;
}
static void setup_rock_ridge(struct iso_directory_record *de,
struct inode *inode, struct rock_state *rs)
{
rs->len = sizeof(struct iso_directory_record) + de->name_len[0];
if (rs->len & 1)
(rs->len)++;
rs->chr = (unsigned char *)de + rs->len;
rs->len = *((unsigned char *)de) - rs->len;
if (rs->len < 0)
rs->len = 0;
if (ISOFS_SB(inode->i_sb)->s_rock_offset != -1) {
rs->len -= ISOFS_SB(inode->i_sb)->s_rock_offset;
rs->chr += ISOFS_SB(inode->i_sb)->s_rock_offset;
if (rs->len < 0)
rs->len = 0;
}
}
static void init_rock_state(struct rock_state *rs, struct inode *inode)
{
memset(rs, 0, sizeof(*rs));
rs->inode = inode;
}
/* Maximum number of Rock Ridge continuation entries */
#define RR_MAX_CE_ENTRIES 32
/*
* Returns 0 if the caller should continue scanning, 1 if the scan must end
* and -ve on error.
*/
static int rock_continue(struct rock_state *rs)
{
int ret = 1;
int blocksize = 1 << rs->inode->i_blkbits;
const int min_de_size = offsetof(struct rock_ridge, u);
kfree(rs->buffer);
rs->buffer = NULL;
if ((unsigned)rs->cont_offset > blocksize - min_de_size ||
(unsigned)rs->cont_size > blocksize ||
(unsigned)(rs->cont_offset + rs->cont_size) > blocksize) {
printk(KERN_NOTICE "rock: corrupted directory entry. "
"extent=%d, offset=%d, size=%d\n",
rs->cont_extent, rs->cont_offset, rs->cont_size);
ret = -EIO;
goto out;
}
if ((unsigned)rs->cont_extent >= ISOFS_SB(rs->inode->i_sb)->s_nzones) {
printk(KERN_NOTICE "rock: corrupted directory entry. "
"extent=%u out of volume (nzones=%lu)\n",
(unsigned)rs->cont_extent,
ISOFS_SB(rs->inode->i_sb)->s_nzones);
ret = -EIO;
goto out;
}
if (rs->cont_extent) {
struct buffer_head *bh;
rs->buffer = kmalloc(rs->cont_size, GFP_KERNEL);
if (!rs->buffer) {
ret = -ENOMEM;
goto out;
}
ret = -EIO;
if (++rs->cont_loops >= RR_MAX_CE_ENTRIES)
goto out;
bh = sb_bread(rs->inode->i_sb, rs->cont_extent);
if (bh) {
memcpy(rs->buffer, bh->b_data + rs->cont_offset,
rs->cont_size);
put_bh(bh);
rs->chr = rs->buffer;
rs->len = rs->cont_size;
rs->cont_extent = 0;
rs->cont_size = 0;
rs->cont_offset = 0;
return 0;
}
printk("Unable to read rock-ridge attributes\n");
}
out:
kfree(rs->buffer);
rs->buffer = NULL;
return ret;
}
/*
* We think there's a record of type `sig' at rs->chr. Parse the signature
* and make sure that there's really room for a record of that type.
*/
static int rock_check_overflow(struct rock_state *rs, int sig)
{
int len;
switch (sig) {
case SIG('S', 'P'):
len = sizeof(struct SU_SP_s);
break;
case SIG('C', 'E'):
len = sizeof(struct SU_CE_s);
break;
case SIG('E', 'R'):
len = sizeof(struct SU_ER_s);
break;
case SIG('R', 'R'):
len = sizeof(struct RR_RR_s);
break;
case SIG('P', 'X'):
len = sizeof(struct RR_PX_s);
break;
case SIG('P', 'N'):
len = sizeof(struct RR_PN_s);
break;
case SIG('S', 'L'):
len = sizeof(struct RR_SL_s);
break;
case SIG('N', 'M'):
len = sizeof(struct RR_NM_s);
break;
case SIG('C', 'L'):
len = sizeof(struct RR_CL_s);
break;
case SIG('P', 'L'):
len = sizeof(struct RR_PL_s);
break;
case SIG('T', 'F'):
len = sizeof(struct RR_TF_s);
break;
case SIG('Z', 'F'):
len = sizeof(struct RR_ZF_s);
break;
default:
len = 0;
break;
}
len += offsetof(struct rock_ridge, u);
if (len > rs->len) {
printk(KERN_NOTICE "rock: directory entry would overflow "
"storage\n");
printk(KERN_NOTICE "rock: sig=0x%02x, size=%d, remaining=%d\n",
sig, len, rs->len);
return -EIO;
}
return 0;
}
/*
* return length of name field; 0: not found, -1: to be ignored
*/
int get_rock_ridge_filename(struct iso_directory_record *de,
char *retname, struct inode *inode)
{
struct rock_state rs;
struct rock_ridge *rr;
int sig;
int retnamlen = 0;
int truncate = 0;
int ret = 0;
char *p;
int len;
if (!ISOFS_SB(inode->i_sb)->s_rock)
return 0;
*retname = 0;
init_rock_state(&rs, inode);
setup_rock_ridge(de, inode, &rs);
repeat:
while (rs.len > 2) { /* There may be one byte for padding somewhere */
rr = (struct rock_ridge *)rs.chr;
/*
* Ignore rock ridge info if rr->len is out of range, but
* don't return -EIO because that would make the file
* invisible.
*/
if (rr->len < 3)
goto out; /* Something got screwed up here */
sig = isonum_721(rs.chr);
if (rock_check_overflow(&rs, sig))
goto eio;
rs.chr += rr->len;
rs.len -= rr->len;
/*
* As above, just ignore the rock ridge info if rr->len
* is bogus.
*/
if (rs.len < 0)
goto out; /* Something got screwed up here */
switch (sig) {
case SIG('R', 'R'):
if ((rr->u.RR.flags[0] & RR_NM) == 0)
goto out;
break;
case SIG('S', 'P'):
if (check_sp(rr, inode))
goto out;
break;
case SIG('C', 'E'):
rs.cont_extent = isonum_733(rr->u.CE.extent);
rs.cont_offset = isonum_733(rr->u.CE.offset);
rs.cont_size = isonum_733(rr->u.CE.size);
break;
case SIG('N', 'M'):
if (truncate)
break;
if (rr->len < 5)
break;
/*
* If the flags are 2 or 4, this indicates '.' or '..'.
* We don't want to do anything with this, because it
* screws up the code that calls us. We don't really
* care anyways, since we can just use the non-RR
* name.
*/
if (rr->u.NM.flags & 6)
break;
if (rr->u.NM.flags & ~1) {
printk("Unsupported NM flag settings (%d)\n",
rr->u.NM.flags);
break;
}
len = rr->len - 5;
if (retnamlen + len > NAME_MAX) {
truncate = 1;
break;
}
p = memchr(rr->u.NM.name, '\0', len);
if (unlikely(p))
len = p - rr->u.NM.name;
memcpy(retname + retnamlen, rr->u.NM.name, len);
retnamlen += len;
retname[retnamlen] = '\0';
break;
case SIG('R', 'E'):
kfree(rs.buffer);
return -1;
default:
break;
}
}
ret = rock_continue(&rs);
if (ret == 0)
goto repeat;
if (ret == 1)
return retnamlen; /* If 0, this file did not have a NM field */
out:
kfree(rs.buffer);
return ret;
eio:
ret = -EIO;
goto out;
}
#define RR_REGARD_XA 1
#define RR_RELOC_DE 2
static int
parse_rock_ridge_inode_internal(struct iso_directory_record *de,
struct inode *inode, int flags)
{
int symlink_len = 0;
int cnt, sig;
unsigned int reloc_block;
struct inode *reloc;
struct rock_ridge *rr;
int rootflag;
struct rock_state rs;
int ret = 0;
if (!ISOFS_SB(inode->i_sb)->s_rock)
return 0;
init_rock_state(&rs, inode);
setup_rock_ridge(de, inode, &rs);
if (flags & RR_REGARD_XA) {
rs.chr += 14;
rs.len -= 14;
if (rs.len < 0)
rs.len = 0;
}
repeat:
while (rs.len > 2) { /* There may be one byte for padding somewhere */
rr = (struct rock_ridge *)rs.chr;
/*
* Ignore rock ridge info if rr->len is out of range, but
* don't return -EIO because that would make the file
* invisible.
*/
if (rr->len < 3)
goto out; /* Something got screwed up here */
sig = isonum_721(rs.chr);
if (rock_check_overflow(&rs, sig))
goto eio;
rs.chr += rr->len;
rs.len -= rr->len;
/*
* As above, just ignore the rock ridge info if rr->len
* is bogus.
*/
if (rs.len < 0)
goto out; /* Something got screwed up here */
switch (sig) {
#ifndef CONFIG_ZISOFS /* No flag for SF or ZF */
case SIG('R', 'R'):
if ((rr->u.RR.flags[0] &
(RR_PX | RR_TF | RR_SL | RR_CL)) == 0)
goto out;
break;
#endif
case SIG('S', 'P'):
if (check_sp(rr, inode))
goto out;
break;
case SIG('C', 'E'):
rs.cont_extent = isonum_733(rr->u.CE.extent);
rs.cont_offset = isonum_733(rr->u.CE.offset);
rs.cont_size = isonum_733(rr->u.CE.size);
break;
case SIG('E', 'R'):
/* Invalid length of ER tag id? */
if (rr->u.ER.len_id + offsetof(struct rock_ridge, u.ER.data) > rr->len)
goto out;
ISOFS_SB(inode->i_sb)->s_rock = 1;
printk(KERN_DEBUG "ISO 9660 Extensions: ");
{
int p;
for (p = 0; p < rr->u.ER.len_id; p++)
printk(KERN_CONT "%c", rr->u.ER.data[p]);
}
printk(KERN_CONT "\n");
break;
case SIG('P', 'X'):
inode->i_mode = isonum_733(rr->u.PX.mode);
set_nlink(inode, isonum_733(rr->u.PX.n_links));
i_uid_write(inode, isonum_733(rr->u.PX.uid));
i_gid_write(inode, isonum_733(rr->u.PX.gid));
break;
case SIG('P', 'N'):
{
int high, low;
high = isonum_733(rr->u.PN.dev_high);
low = isonum_733(rr->u.PN.dev_low);
/*
* The Rock Ridge standard specifies that if
* sizeof(dev_t) <= 4, then the high field is
* unused, and the device number is completely
* stored in the low field. Some writers may
* ignore this subtlety,
* and as a result we test to see if the entire
* device number is
* stored in the low field, and use that.
*/
if ((low & ~0xff) && high == 0) {
inode->i_rdev =
MKDEV(low >> 8, low & 0xff);
} else {
inode->i_rdev =
MKDEV(high, low);
}
}
break;
case SIG('T', 'F'): {
int flags, size, slen;
flags = rr->u.TF.flags & TF_LONG_FORM ? ISO_DATE_LONG_FORM : 0;
size = rr->u.TF.flags & TF_LONG_FORM ? 17 : 7;
slen = rr->len - 5;
/*
* Some RRIP writers incorrectly place ctime in the
* TF_CREATE field. Try to handle this correctly for
* either case.
*/
/* Rock ridge never appears on a High Sierra disk */
cnt = 0;
if ((rr->u.TF.flags & TF_CREATE) && size <= slen) {
inode_set_ctime_to_ts(inode,
iso_date(rr->u.TF.data + size * cnt++, flags));
slen -= size;
}
if ((rr->u.TF.flags & TF_MODIFY) && size <= slen) {
inode_set_mtime_to_ts(inode,
iso_date(rr->u.TF.data + size * cnt++, flags));
slen -= size;
}
if ((rr->u.TF.flags & TF_ACCESS) && size <= slen) {
inode_set_atime_to_ts(inode,
iso_date(rr->u.TF.data + size * cnt++, flags));
slen -= size;
}
if ((rr->u.TF.flags & TF_ATTRIBUTES) && size <= slen) {
inode_set_ctime_to_ts(inode,
iso_date(rr->u.TF.data + size * cnt++, flags));
slen -= size;
}
break;
}
case SIG('S', 'L'):
{
int slen;
struct SL_component *slp;
struct SL_component *oldslp;
slen = rr->len - 5;
slp = &rr->u.SL.link;
inode->i_size = symlink_len;
while (slen > 1) {
rootflag = 0;
/* keep the component within the SL record */
if (slp->len + 2 > slen)
goto eio;
switch (slp->flags & ~1) {
case 0:
inode->i_size +=
slp->len;
break;
case 2:
inode->i_size += 1;
break;
case 4:
inode->i_size += 2;
break;
case 8:
rootflag = 1;
inode->i_size += 1;
break;
default:
printk("Symlink component flag "
"not implemented\n");
}
slen -= slp->len + 2;
oldslp = slp;
slp = (struct SL_component *)
(((char *)slp) + slp->len + 2);
if (slen < 2) {
if (((rr->u.SL.
flags & 1) != 0)
&&
((oldslp->
flags & 1) == 0))
inode->i_size +=
1;
break;
}
/*
* If this component record isn't
* continued, then append a '/'.
*/
if (!rootflag
&& (oldslp->flags & 1) == 0)
inode->i_size += 1;
}
}
symlink_len = inode->i_size;
break;
case SIG('R', 'E'):
printk(KERN_WARNING "Attempt to read inode for "
"relocated directory\n");
goto out;
case SIG('C', 'L'):
if (flags & RR_RELOC_DE) {
printk(KERN_ERR
"ISOFS: Recursive directory relocation "
"is not supported\n");
goto eio;
}
reloc_block = isonum_733(rr->u.CL.location);
if (reloc_block == ISOFS_I(inode)->i_iget5_block &&
ISOFS_I(inode)->i_iget5_offset == 0) {
printk(KERN_ERR
"ISOFS: Directory relocation points to "
"itself\n");
goto eio;
}
ISOFS_I(inode)->i_first_extent = reloc_block;
reloc = isofs_iget_reloc(inode->i_sb, reloc_block, 0);
if (IS_ERR(reloc)) {
ret = PTR_ERR(reloc);
goto out;
}
inode->i_mode = reloc->i_mode;
set_nlink(inode, reloc->i_nlink);
inode->i_uid = reloc->i_uid;
inode->i_gid = reloc->i_gid;
inode->i_rdev = reloc->i_rdev;
inode->i_size = reloc->i_size;
inode->i_blocks = reloc->i_blocks;
inode_set_atime_to_ts(inode, inode_get_atime(reloc));
inode_set_ctime_to_ts(inode, inode_get_ctime(reloc));
inode_set_mtime_to_ts(inode, inode_get_mtime(reloc));
iput(reloc);
break;
#ifdef CONFIG_ZISOFS
case SIG('Z', 'F'): {
int algo;
if (ISOFS_SB(inode->i_sb)->s_nocompress)
break;
algo = isonum_721(rr->u.ZF.algorithm);
if (algo == SIG('p', 'z')) {
int block_shift =
isonum_711(&rr->u.ZF.parms[1]);
if (block_shift > 17) {
printk(KERN_WARNING "isofs: "
"Can't handle ZF block "
"size of 2^%d\n",
block_shift);
} else {
/*
* Note: we don't change
* i_blocks here
*/
ISOFS_I(inode)->i_file_format =
isofs_file_compressed;
/*
* Parameters to compression
* algorithm (header size,
* block size)
*/
ISOFS_I(inode)->i_format_parm[0] =
isonum_711(&rr->u.ZF.parms[0]);
ISOFS_I(inode)->i_format_parm[1] =
isonum_711(&rr->u.ZF.parms[1]);
inode->i_size =
isonum_733(rr->u.ZF.
real_size);
}
} else {
printk(KERN_WARNING
"isofs: Unknown ZF compression "
"algorithm: %c%c\n",
rr->u.ZF.algorithm[0],
rr->u.ZF.algorithm[1]);
}
break;
}
#endif
default:
break;
}
}
ret = rock_continue(&rs);
if (ret == 0)
goto repeat;
if (ret == 1)
ret = 0;
out:
kfree(rs.buffer);
return ret;
eio:
ret = -EIO;
goto out;
}
static char *get_symlink_chunk(char *rpnt, struct rock_ridge *rr, char *plimit)
{
int slen;
int rootflag;
struct SL_component *oldslp;
struct SL_component *slp;
slen = rr->len - 5;
slp = &rr->u.SL.link;
while (slen > 1) {
rootflag = 0;
/*
* A component is slp->len + 2 bytes (a two-byte header plus
* len bytes of text). If it does not fit in the bytes left in
* the SL record the record is malformed: fail like the plimit
* checks below so readlink() returns -EIO, not a truncated path.
*/
if (slp->len + 2 > slen)
return NULL;
switch (slp->flags & ~1) {
case 0:
if (slp->len > plimit - rpnt)
return NULL;
memcpy(rpnt, slp->text, slp->len);
rpnt += slp->len;
break;
case 2:
if (rpnt >= plimit)
return NULL;
*rpnt++ = '.';
break;
case 4:
if (2 > plimit - rpnt)
return NULL;
*rpnt++ = '.';
*rpnt++ = '.';
break;
case 8:
if (rpnt >= plimit)
return NULL;
rootflag = 1;
*rpnt++ = '/';
break;
default:
printk("Symlink component flag not implemented (%d)\n",
slp->flags);
}
slen -= slp->len + 2;
oldslp = slp;
slp = (struct SL_component *)((char *)slp + slp->len + 2);
if (slen < 2) {
/*
* If there is another SL record, and this component
* record isn't continued, then add a slash.
*/
if ((!rootflag) && (rr->u.SL.flags & 1) &&
!(oldslp->flags & 1)) {
if (rpnt >= plimit)
return NULL;
*rpnt++ = '/';
}
break;
}
/*
* If this component record isn't continued, then append a '/'.
*/
if (!rootflag && !(oldslp->flags & 1)) {
if (rpnt >= plimit)
return NULL;
*rpnt++ = '/';
}
}
return rpnt;
}
int parse_rock_ridge_inode(struct iso_directory_record *de, struct inode *inode,
int relocated)
{
int flags = relocated ? RR_RELOC_DE : 0;
int result = parse_rock_ridge_inode_internal(de, inode, flags);
/*
* if rockridge flag was reset and we didn't look for attributes
* behind eventual XA attributes, have a look there
*/
if ((ISOFS_SB(inode->i_sb)->s_rock_offset == -1)
&& (ISOFS_SB(inode->i_sb)->s_rock == 2)) {
result = parse_rock_ridge_inode_internal(de, inode,
flags | RR_REGARD_XA);
}
return result;
}
/*
* read_folio() for symlinks: reads symlink contents into the folio and either
* makes it uptodate and returns 0 or returns error (-EIO)
*/
static int rock_ridge_symlink_read_folio(struct file *file, struct folio *folio)
{
struct inode *inode = folio->mapping->host;
struct iso_inode_info *ei = ISOFS_I(inode);
struct isofs_sb_info *sbi = ISOFS_SB(inode->i_sb);
char *link = folio_address(folio);
unsigned long bufsize = ISOFS_BUFFER_SIZE(inode);
struct buffer_head *bh;
char *rpnt = link;
unsigned char *pnt;
struct iso_directory_record *raw_de;
unsigned long block, offset;
int sig;
struct rock_ridge *rr;
struct rock_state rs;
int ret;
if (!sbi->s_rock)
goto error;
init_rock_state(&rs, inode);
block = ei->i_iget5_block;
bh = sb_bread(inode->i_sb, block);
if (!bh)
goto out_noread;
offset = ei->i_iget5_offset;
pnt = (unsigned char *)bh->b_data + offset;
raw_de = (struct iso_directory_record *)pnt;
/*
* If we go past the end of the buffer, there is some sort of error.
*/
if (offset + *pnt > bufsize)
goto out_bad_span;
/*
* Now test for possible Rock Ridge extensions which will override
* some of these numbers in the inode structure.
*/
setup_rock_ridge(raw_de, inode, &rs);
repeat:
while (rs.len > 2) { /* There may be one byte for padding somewhere */
rr = (struct rock_ridge *)rs.chr;
if (rr->len < 3)
goto out; /* Something got screwed up here */
sig = isonum_721(rs.chr);
if (rock_check_overflow(&rs, sig))
goto out;
rs.chr += rr->len;
rs.len -= rr->len;
if (rs.len < 0)
goto out; /* corrupted isofs */
switch (sig) {
case SIG('R', 'R'):
if ((rr->u.RR.flags[0] & RR_SL) == 0)
goto out;
break;
case SIG('S', 'P'):
if (check_sp(rr, inode))
goto out;
break;
case SIG('S', 'L'):
rpnt = get_symlink_chunk(rpnt, rr,
link + (PAGE_SIZE - 1));
if (rpnt == NULL)
goto out;
break;
case SIG('C', 'E'):
/* This tells is if there is a continuation record */
rs.cont_extent = isonum_733(rr->u.CE.extent);
rs.cont_offset = isonum_733(rr->u.CE.offset);
rs.cont_size = isonum_733(rr->u.CE.size);
break;
default:
break;
}
}
ret = rock_continue(&rs);
if (ret == 0)
goto repeat;
if (ret < 0)
goto fail;
if (rpnt == link)
goto fail;
brelse(bh);
*rpnt = '\0';
ret = 0;
end:
folio_end_read(folio, ret == 0);
return ret;
/* error exit from macro */
out:
kfree(rs.buffer);
goto fail;
out_noread:
printk("unable to read i-node block");
goto fail;
out_bad_span:
printk("symlink spans iso9660 blocks\n");
fail:
brelse(bh);
error:
ret = -EIO;
goto end;
}
const struct address_space_operations isofs_symlink_aops = {
.read_folio = rock_ridge_symlink_read_folio
};
+119
View File
@@ -0,0 +1,119 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* These structs are used by the system-use-sharing protocol, in which the
* Rock Ridge extensions are embedded. It is quite possible that other
* extensions are present on the disk, and this is fine as long as they
* all use SUSP
*/
struct SU_SP_s {
__u8 magic[2];
__u8 skip;
} __attribute__ ((packed));
struct SU_CE_s {
__u8 extent[8];
__u8 offset[8];
__u8 size[8];
};
struct SU_ER_s {
__u8 len_id;
__u8 len_des;
__u8 len_src;
__u8 ext_ver;
__u8 data[];
} __attribute__ ((packed));
struct RR_RR_s {
__u8 flags[1];
} __attribute__ ((packed));
struct RR_PX_s {
__u8 mode[8];
__u8 n_links[8];
__u8 uid[8];
__u8 gid[8];
};
struct RR_PN_s {
__u8 dev_high[8];
__u8 dev_low[8];
};
struct SL_component {
__u8 flags;
__u8 len;
__u8 text[] __counted_by(len);
} __attribute__ ((packed));
struct RR_SL_s {
__u8 flags;
struct SL_component link;
} __attribute__ ((packed));
struct RR_NM_s {
__u8 flags;
char name[];
} __attribute__ ((packed));
struct RR_CL_s {
__u8 location[8];
};
struct RR_PL_s {
__u8 location[8];
};
struct RR_TF_s {
__u8 flags;
__u8 data[];
} __attribute__ ((packed));
/* Linux-specific extension for transparent decompression */
struct RR_ZF_s {
__u8 algorithm[2];
__u8 parms[2];
__u8 real_size[8];
};
/*
* These are the bits and their meanings for flags in the TF structure.
*/
#define TF_CREATE 1
#define TF_MODIFY 2
#define TF_ACCESS 4
#define TF_ATTRIBUTES 8
#define TF_BACKUP 16
#define TF_EXPIRATION 32
#define TF_EFFECTIVE 64
#define TF_LONG_FORM 128
struct rock_ridge {
__u8 signature[2];
__u8 len;
__u8 version;
union {
struct SU_SP_s SP;
struct SU_CE_s CE;
struct SU_ER_s ER;
struct RR_RR_s RR;
struct RR_PX_s PX;
struct RR_PN_s PN;
struct RR_SL_s SL;
struct RR_NM_s NM;
struct RR_CL_s CL;
struct RR_PL_s PL;
struct RR_TF_s TF;
struct RR_ZF_s ZF;
} u;
};
#define RR_PX 1 /* POSIX attributes */
#define RR_PN 2 /* POSIX devices */
#define RR_SL 4 /* Symbolic link */
#define RR_NM 8 /* Alternate Name */
#define RR_CL 16 /* Child link */
#define RR_PL 32 /* Parent link */
#define RR_RE 64 /* Relocation directory */
#define RR_TF 128 /* Timestamps */
+86
View File
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: GPL-2.0
/*
* linux/fs/isofs/util.c
*/
#include <linux/time.h>
#include "isofs.h"
/*
* We have to convert from a MM/DD/YY format to the Unix ctime format.
* We have to take into account leap years and all of that good stuff.
* Unfortunately, the kernel does not have the information on hand to
* take into account daylight savings time, but it shouldn't matter.
* The time stored should be localtime (with or without DST in effect),
* and the timezone offset should hold the offset required to get back
* to GMT. Thus we should always be correct.
*/
struct timespec64 iso_date(u8 *p, int flags)
{
int year, month, day, hour, minute, second, tz;
struct timespec64 ts;
if (flags & ISO_DATE_LONG_FORM) {
year = (p[0] - '0') * 1000 +
(p[1] - '0') * 100 +
(p[2] - '0') * 10 +
(p[3] - '0') - 1900;
month = ((p[4] - '0') * 10 + (p[5] - '0'));
day = ((p[6] - '0') * 10 + (p[7] - '0'));
hour = ((p[8] - '0') * 10 + (p[9] - '0'));
minute = ((p[10] - '0') * 10 + (p[11] - '0'));
second = ((p[12] - '0') * 10 + (p[13] - '0'));
ts.tv_nsec = ((p[14] - '0') * 10 + (p[15] - '0')) * 10000000;
tz = p[16];
} else {
year = p[0];
month = p[1];
day = p[2];
hour = p[3];
minute = p[4];
second = p[5];
ts.tv_nsec = 0;
/* High sierra has no time zone */
tz = flags & ISO_DATE_HIGH_SIERRA ? 0 : p[6];
}
if (year < 0) {
ts.tv_sec = 0;
} else {
ts.tv_sec = mktime64(year+1900, month, day, hour, minute, second);
/* sign extend */
if (tz & 0x80)
tz |= (-1 << 8);
/*
* The timezone offset is unreliable on some disks,
* so we make a sanity check. In no case is it ever
* more than 13 hours from GMT, which is 52*15min.
* The time is always stored in localtime with the
* timezone offset being what get added to GMT to
* get to localtime. Thus we need to subtract the offset
* to get to true GMT, which is what we store the time
* as internally. On the local system, the user may set
* their timezone any way they wish, of course, so GMT
* gets converted back to localtime on the receiving
* system.
*
* NOTE: mkisofs in versions prior to mkisofs-1.10 had
* the sign wrong on the timezone offset. This has now
* been corrected there too, but if you are getting screwy
* results this may be the explanation. If enough people
* complain, a user configuration option could be added
* to add the timezone offset in with the wrong sign
* for 'compatibility' with older discs, but I cannot see how
* it will matter that much.
*
* Thanks to kuhlmav@elec.canterbury.ac.nz (Volker Kuhlmann)
* for pointing out the sign error.
*/
if (-52 <= tz && tz <= 52)
ts.tv_sec -= tz * 15 * 60;
}
return ts;
}
+16
View File
@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* ----------------------------------------------------------------------- *
*
* Copyright 2001 H. Peter Anvin - All Rights Reserved
*
* ----------------------------------------------------------------------- */
/*
* Prototypes for functions exported from the compressed isofs subsystem
*/
#ifdef CONFIG_ZISOFS
extern const struct address_space_operations zisofs_aops;
extern int __init zisofs_init(void);
extern void zisofs_cleanup(void);
#endif