// SPDX-License-Identifier: GPL-2.0-only /* * inode.c * * PURPOSE * Inode handling routines for the OSTA-UDF(tm) filesystem. * * COPYRIGHT * (C) 1998 Dave Boynton * (C) 1998-2004 Ben Fennema * (C) 1999-2000 Stelias Computing Inc * * HISTORY * * 10/04/98 dgb Added rudimentary directory functions * 10/07/98 Fully working udf_block_map! It works! * 11/25/98 bmap altered to better support extents * 12/06/98 blf partition support in udf_iget, udf_block_map * and udf_read_inode * 12/12/98 rewrote udf_block_map to handle next extents and descs across * block boundaries (which is not actually allowed) * 12/20/98 added support for strategy 4096 * 03/07/99 rewrote udf_block_map (again) * New funcs, inode_bmap, udf_next_aext * 04/19/99 Support for writing device EA's for major/minor #
*/
/* * No readahead needed for in-ICB files and udf_get_block() would get * confused for such file anyway.
*/ if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) return;
/* * Expand file stored in ICB to a normal one-block-file * * This function requires i_mutex held
*/ int udf_expand_file_adinicb(struct inode *inode)
{ struct folio *folio; struct udf_inode_info *iinfo = UDF_I(inode); int err;
WARN_ON_ONCE(!inode_is_locked(inode)); if (!iinfo->i_lenAlloc) {
down_write(&iinfo->i_data_sem); 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;
up_write(&iinfo->i_data_sem);
mark_inode_dirty(inode); return 0;
}
down_write(&iinfo->i_data_sem); /* * Block beyond EOF and prealloc extents? Just discard preallocation * as it is not useful and complicates things.
*/ if (((loff_t)map->lblk) << inode->i_blkbits >= iinfo->i_lenExtents)
udf_discard_prealloc(inode);
udf_clear_extent_cache(inode);
ret = inode_getblk(inode, map);
up_write(&iinfo->i_data_sem); return ret;
}
err = udf_map_block(inode, &map); if (err < 0) return err; if (map.oflags & UDF_BLK_MAPPED) {
map_bh(bh_result, inode->i_sb, map.pblk); if (map.oflags & UDF_BLK_NEW)
set_buffer_new(bh_result);
} return 0;
}
int udf_get_block(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create)
{ int flags = create ? UDF_MAP_CREATE : 0;
/* * We preallocate blocks only for regular files. It also makes sense * for directories but there's a problem when to drop the * preallocation. We might use some delayed work for that but I feel * it's overengineering for a filesystem like UDF.
*/ if (!S_ISREG(inode->i_mode))
flags |= UDF_MAP_NOPREALLOC; return __udf_get_block(inode, block, bh_result, flags);
}
/* * We shouldn't be allocating blocks on page writeback since we allocate them * on page fault. We can spot dirty buffers without allocated blocks though * when truncate expands file. These however don't have valid data so we can * safely ignore them. So never allocate blocks from page writeback.
*/ staticint udf_get_block_wb(struct inode *inode, sector_t block, struct buffer_head *bh_result, int create)
{ return __udf_get_block(inode, block, bh_result, 0);
}
/* Extend the file with new blocks totaling 'new_block_bytes', * return the number of extents added
*/ staticint udf_do_extend_file(struct inode *inode, struct extent_position *last_pos, struct kernel_long_ad *last_ext,
loff_t new_block_bytes)
{
uint32_t add; int count = 0, fake = !(last_ext->extLength & UDF_EXTENT_LENGTH_MASK); struct super_block *sb = inode->i_sb; struct udf_inode_info *iinfo; int err;
/* The previous extent is fake and we should not extend by anything
* - there's nothing to do... */ if (!new_block_bytes && fake) return 0;
iinfo = UDF_I(inode); /* Round the last extent up to a multiple of block size */ if (last_ext->extLength & (sb->s_blocksize - 1)) {
last_ext->extLength =
(last_ext->extLength & UDF_EXTENT_FLAG_MASK) |
(((last_ext->extLength & UDF_EXTENT_LENGTH_MASK) +
sb->s_blocksize - 1) & ~(sb->s_blocksize - 1));
iinfo->i_lenExtents =
(iinfo->i_lenExtents + sb->s_blocksize - 1) &
~(sb->s_blocksize - 1);
}
add = 0; /* Can we merge with the previous extent? */ if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) ==
EXT_NOT_RECORDED_NOT_ALLOCATED) {
add = (1 << 30) - sb->s_blocksize -
(last_ext->extLength & UDF_EXTENT_LENGTH_MASK); if (add > new_block_bytes)
add = new_block_bytes;
new_block_bytes -= add;
last_ext->extLength += add;
}
/* * We've rewritten the last extent. If we are going to add * more extents, we may need to enter possible following * empty indirect extent.
*/ if (new_block_bytes) {
err = udf_next_aext(inode, last_pos, &tmploc, &tmplen,
&tmptype, 0); if (err < 0) goto out_err;
}
}
iinfo->i_lenExtents += add;
/* Managed to do everything necessary? */ if (!new_block_bytes) goto out;
/* All further extents will be NOT_RECORDED_NOT_ALLOCATED */
last_ext->extLocation.logicalBlockNum = 0;
last_ext->extLocation.partitionReferenceNum = 0;
add = (1 << 30) - sb->s_blocksize;
last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | add;
/* Create enough extents to cover the whole hole */ while (new_block_bytes > add) {
new_block_bytes -= add;
err = udf_add_aext(inode, last_pos, &last_ext->extLocation,
last_ext->extLength, 1); if (err) goto out_err;
iinfo->i_lenExtents += add;
count++;
} if (new_block_bytes) {
last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
new_block_bytes;
err = udf_add_aext(inode, last_pos, &last_ext->extLocation,
last_ext->extLength, 1); if (err) goto out_err;
iinfo->i_lenExtents += new_block_bytes;
count++;
}
out: /* last_pos should point to the last written extent... */ if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
last_pos->offset -= sizeof(struct short_ad); elseif (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
last_pos->offset -= sizeof(struct long_ad); else return -EIO;
return count;
out_err: /* Remove extents we've created so far */
udf_clear_extent_cache(inode);
udf_truncate_extents(inode); return err;
}
/* Extend the final block of the file to final_block_len bytes */ staticvoid udf_do_extend_final_block(struct inode *inode, struct extent_position *last_pos, struct kernel_long_ad *last_ext,
uint32_t new_elen)
{
uint32_t added_bytes;
/* * Extent already large enough? It may be already rounded up to block * size...
*/ if (new_elen <= (last_ext->extLength & UDF_EXTENT_LENGTH_MASK)) return;
added_bytes = new_elen - (last_ext->extLength & UDF_EXTENT_LENGTH_MASK);
last_ext->extLength += added_bytes;
UDF_I(inode)->i_lenExtents += added_bytes;
down_write(&iinfo->i_data_sem); /* * When creating hole in file, just don't bother with preserving * preallocation. It likely won't be very useful anyway.
*/
udf_discard_prealloc(inode);
/* File has extent covering the new size (could happen when extending * inside a block)?
*/ if (within_last_ext) { /* Extending file within the last file block */
udf_do_extend_final_block(inode, &epos, &extent, new_elen);
} else {
err = udf_do_extend_file(inode, &epos, &extent, new_elen);
}
/* find the extent which contains the block we are looking for. alternate between laarr[0] and laarr[1] for locations of the
current extent, and the previous extent */ do { if (prev_epos.bh != cur_epos.bh) {
brelse(prev_epos.bh);
get_bh(cur_epos.bh);
prev_epos.bh = cur_epos.bh;
} if (cur_epos.bh != next_epos.bh) {
brelse(cur_epos.bh);
get_bh(next_epos.bh);
cur_epos.bh = next_epos.bh;
}
b_off -= lbcount;
offset = b_off >> inode->i_sb->s_blocksize_bits; /* * Move prev_epos and cur_epos into indirect extent if we are at * the pointer to it
*/
ret = udf_next_aext(inode, &prev_epos, &tmpeloc, &tmpelen, &tmpetype, 0); if (ret < 0) goto out_free;
ret = udf_next_aext(inode, &cur_epos, &tmpeloc, &tmpelen, &tmpetype, 0); if (ret < 0) goto out_free;
/* if the extent is allocated and recorded, return the block
if the extent is not a multiple of the blocksize, round up */
/* Are we beyond EOF and preallocated extent? */ if (isBeyondEOF) {
loff_t hole_len;
if (count) { if (c)
laarr[0] = laarr[1];
startnum = 1;
} else { /* Create a fake extent when there's not one */
memset(&laarr[0].extLocation, 0x00, sizeof(struct kernel_lb_addr));
laarr[0].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED; /* Will udf_do_extend_file() create real extent from
a fake one? */
startnum = (offset > 0);
} /* Create extents for the hole between EOF and offset */
hole_len = (loff_t)offset << inode->i_blkbits;
ret = udf_do_extend_file(inode, &prev_epos, laarr, hole_len); if (ret < 0) goto out_free;
c = 0;
offset = 0;
count += ret; /* * Is there any real extent? - otherwise we overwrite the fake * one...
*/ if (count)
c = !c;
laarr[c].extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
inode->i_sb->s_blocksize;
memset(&laarr[c].extLocation, 0x00, sizeof(struct kernel_lb_addr));
count++;
endnum = c + 1;
lastblock = 1;
} else {
endnum = startnum = ((count > 2) ? 2 : count);
/* if the current extent is in position 0,
swap it with the previous */ if (!c && count != 1) {
laarr[2] = laarr[0];
laarr[0] = laarr[1];
laarr[1] = laarr[2];
c = 1;
}
/* if the current block is located in an extent,
read the next extent */
ret = udf_next_aext(inode, &next_epos, &eloc, &elen, &etype, 0); if (ret > 0) {
laarr[c + 1].extLength = (etype << 30) | elen;
laarr[c + 1].extLocation = eloc;
count++;
startnum++;
endnum++;
} elseif (ret == 0)
lastblock = 1; else goto out_free;
}
/* if the current extent is not recorded but allocated, get the
* block in the extent corresponding to the requested block */ if ((laarr[c].extLength >> 30) == (EXT_NOT_RECORDED_ALLOCATED >> 30))
newblocknum = laarr[c].extLocation.logicalBlockNum + offset; else { /* otherwise, allocate a new block */ if (iinfo->i_next_alloc_block == map->lblk)
goal = iinfo->i_next_alloc_goal;
if (!goal) { if (!(goal = pgoal)) /* XXX: what was intended here? */
goal = iinfo->i_location.logicalBlockNum + 1;
}
newblocknum = udf_new_block(inode->i_sb, inode,
iinfo->i_location.partitionReferenceNum,
goal, &ret); if (!newblocknum) goto out_free; if (isBeyondEOF)
iinfo->i_lenExtents += inode->i_sb->s_blocksize;
}
/* if the extent the requsted block is located in contains multiple * blocks, split the extent into at most three extents. blocks prior * to requested block, requested block, and blocks after requested
* block */
udf_split_extents(inode, &c, offset, newblocknum, laarr, &endnum);
if (!(map->iflags & UDF_MAP_NOPREALLOC))
udf_prealloc_extents(inode, c, lastblock, laarr, &endnum);
/* merge any continuous blocks in laarr */
udf_merge_extents(inode, laarr, &endnum);
/* write back the new extents, inserting new extents if the new number * of extents is greater than the old number, and deleting extents if
* the new number of extents is less than the old number */
ret = udf_update_extents(inode, laarr, startnum, endnum, &prev_epos); if (ret < 0) goto out_free;
staticint udf_update_extents(struct inode *inode, struct kernel_long_ad *laarr, int startnum, int endnum, struct extent_position *epos)
{ int start = 0, i; struct kernel_lb_addr tmploc;
uint32_t tmplen;
int8_t tmpetype; int err;
if (startnum > endnum) { for (i = 0; i < (startnum - endnum); i++)
udf_delete_aext(inode, *epos);
} elseif (startnum < endnum) { for (i = 0; i < (endnum - startnum); i++) {
err = udf_insert_aext(inode, *epos,
laarr[i].extLocation,
laarr[i].extLength); /* * If we fail here, we are likely corrupting the extent * list and leaking blocks. At least stop early to * limit the damage.
*/ if (err < 0) return err;
err = udf_next_aext(inode, epos, &laarr[i].extLocation,
&laarr[i].extLength, &tmpetype, 1); if (err < 0) return err;
start++;
}
}
for (i = start; i < endnum; i++) {
err = udf_next_aext(inode, epos, &tmploc, &tmplen, &tmpetype, 0); if (err < 0) return err;
/* * Maximum length of linked list formed by ICB hierarchy. The chosen number is * arbitrary - just that we hopefully don't limit any real use of rewritten * inode on write-once media but avoid looping for too long on corrupted media.
*/ #define UDF_MAX_ICB_NESTING 1024
if (iloc->logicalBlockNum >=
sbi->s_partmaps[iloc->partitionReferenceNum].s_partition_len) {
udf_debug("block=%u, partition=%u out of range\n",
iloc->logicalBlockNum, iloc->partitionReferenceNum); return -EIO;
}
/* * Set defaults, but the inode is still incomplete! * Note: get_new_inode() sets the following on a new inode: * i_sb = sb * i_no = ino * i_flags = sb->s_flags * i_state = 0 * clean_inode(): zero fills and sets * i_count = 1 * i_nlink = 1 * i_op = NULL;
*/
bh = udf_read_ptagged(inode->i_sb, iloc, 0, &ident); if (!bh) {
udf_err(inode->i_sb, "(ino %lu) failed !bh\n", inode->i_ino); return -EIO;
}
/* * Sanity check length of allocation descriptors and extended attrs to * avoid integer overflows
*/ if (iinfo->i_lenEAttr > bs || iinfo->i_lenAlloc > bs) goto out; /* Now do exact checks */ if (udf_file_entry_alloc_offset(inode) + iinfo->i_lenAlloc > bs) goto out; /* Sanity checks for files in ICB so that we don't get confused later */ if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) { /* * For file in ICB data is stored in allocation descriptor * so sizes should match
*/ if (iinfo->i_lenAlloc != inode->i_size) goto out; /* File in ICB has to fit in there... */ if (inode->i_size > bs - udf_file_entry_alloc_offset(inode)) goto out;
}
switch (fe->icbTag.fileType) { case ICBTAG_FILE_TYPE_DIRECTORY:
inode->i_op = &udf_dir_inode_operations;
inode->i_fop = &udf_dir_operations;
inode->i_mode |= S_IFDIR;
inc_nlink(inode); break; case ICBTAG_FILE_TYPE_REALTIME: case ICBTAG_FILE_TYPE_REGULAR: case ICBTAG_FILE_TYPE_UNDEF: case ICBTAG_FILE_TYPE_VAT20:
inode->i_data.a_ops = &udf_aops;
inode->i_op = &udf_file_inode_operations;
inode->i_fop = &udf_file_operations;
inode->i_mode |= S_IFREG; break; case ICBTAG_FILE_TYPE_BLOCK:
inode->i_mode |= S_IFBLK; break; case ICBTAG_FILE_TYPE_CHAR:
inode->i_mode |= S_IFCHR; break; case ICBTAG_FILE_TYPE_FIFO:
init_special_inode(inode, inode->i_mode | S_IFIFO, 0); break; case ICBTAG_FILE_TYPE_SOCKET:
init_special_inode(inode, inode->i_mode | S_IFSOCK, 0); break; case ICBTAG_FILE_TYPE_SYMLINK:
inode->i_data.a_ops = &udf_symlink_aops;
inode->i_op = &udf_symlink_inode_operations;
inode_nohighmem(inode);
inode->i_mode = S_IFLNK | 0777; break; case ICBTAG_FILE_TYPE_MAIN:
udf_debug("METADATA FILE-----\n"); break; case ICBTAG_FILE_TYPE_MIRROR:
udf_debug("METADATA MIRROR FILE-----\n"); break; case ICBTAG_FILE_TYPE_BITMAP:
udf_debug("METADATA BITMAP FILE-----\n"); break; default:
udf_err(inode->i_sb, "(ino %lu) failed unknown file type=%u\n",
inode->i_ino, fe->icbTag.fileType); goto out;
} if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) { struct deviceSpec *dsea =
(struct deviceSpec *)udf_get_extendedattr(inode, 12, 1); if (dsea) {
init_special_inode(inode, inode->i_mode,
MKDEV(le32_to_cpu(dsea->majorDeviceIdent),
le32_to_cpu(dsea->minorDeviceIdent))); /* Developer ID ??? */
} else goto out;
}
ret = 0;
out:
brelse(bh); return ret;
}
/* * Do we have to copy current last extent to make space for indirect * one?
*/ if (epos->offset + adsize > sb->s_blocksize) { struct kernel_lb_addr cp_loc;
uint32_t cp_len;
int8_t cp_type;
/* * Append extent at the given position - should be the first free one in inode * / indirect extent. This function assumes there is enough space in the inode * or indirect extent. Use udf_add_aext() if you didn't check for this before.
*/ int __udf_add_aext(struct inode *inode, struct extent_position *epos, struct kernel_lb_addr *eloc, uint32_t elen, int inc)
{ struct udf_inode_info *iinfo = UDF_I(inode); struct allocExtDesc *aed; int adsize;
/* * Append extent at given position - should be the first free one in inode * / indirect extent. Takes care of allocating and linking indirect blocks.
*/ int udf_add_aext(struct inode *inode, struct extent_position *epos, struct kernel_lb_addr *eloc, uint32_t elen, int inc)
{ int adsize; struct super_block *sb = inode->i_sb;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.