summaryrefslogtreecommitdiff
path: root/fs/erofs/data.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/erofs/data.c')
-rw-r--r--fs/erofs/data.c165
1 files changed, 105 insertions, 60 deletions
diff --git a/fs/erofs/data.c b/fs/erofs/data.c
index 761896054c8..f4b21d7917a 100644
--- a/fs/erofs/data.c
+++ b/fs/erofs/data.c
@@ -12,23 +12,23 @@ static int erofs_map_blocks_flatmode(struct erofs_inode *inode,
struct erofs_inode *vi = inode;
bool tailendpacking = (vi->datalayout == EROFS_INODE_FLAT_INLINE);
- nblocks = DIV_ROUND_UP(inode->i_size, EROFS_BLKSIZ);
+ nblocks = BLK_ROUND_UP(inode->i_size);
lastblk = nblocks - tailendpacking;
/* there is no hole in flatmode */
map->m_flags = EROFS_MAP_MAPPED;
- if (offset < blknr_to_addr(lastblk)) {
- map->m_pa = blknr_to_addr(vi->u.i_blkaddr) + map->m_la;
- map->m_plen = blknr_to_addr(lastblk) - offset;
+ if (offset < erofs_pos(lastblk)) {
+ map->m_pa = erofs_pos(vi->u.i_blkaddr) + map->m_la;
+ map->m_plen = erofs_pos(lastblk) - offset;
} else if (tailendpacking) {
/* 2 - inode inline B: inode, [xattrs], inline last blk... */
map->m_pa = iloc(vi->nid) + vi->inode_isize +
vi->xattr_isize + erofs_blkoff(map->m_la);
map->m_plen = inode->i_size - offset;
- /* inline data should be located in one meta block */
- if (erofs_blkoff(map->m_pa) + map->m_plen > PAGE_SIZE) {
+ /* inline data should be located in the same meta block */
+ if (erofs_blkoff(map->m_pa) + map->m_plen > erofs_blksiz()) {
erofs_err("inline data cross block boundary @ nid %" PRIu64,
vi->nid);
DBG_BUGON(1);
@@ -55,7 +55,7 @@ int erofs_map_blocks(struct erofs_inode *inode,
{
struct erofs_inode *vi = inode;
struct erofs_inode_chunk_index *idx;
- u8 buf[EROFS_BLKSIZ];
+ u8 buf[EROFS_MAX_BLOCK_SIZE];
u64 chunknr;
unsigned int unit;
erofs_off_t pos;
@@ -87,7 +87,7 @@ int erofs_map_blocks(struct erofs_inode *inode,
map->m_la = chunknr << vi->u.chunkbits;
map->m_plen = min_t(erofs_off_t, 1UL << vi->u.chunkbits,
- roundup(inode->i_size - map->m_la, EROFS_BLKSIZ));
+ roundup(inode->i_size - map->m_la, erofs_blksiz()));
/* handle block map */
if (!(vi->u.chunkformat & EROFS_CHUNK_FORMAT_INDEXES)) {
@@ -96,7 +96,7 @@ int erofs_map_blocks(struct erofs_inode *inode,
if (le32_to_cpu(*blkaddr) == EROFS_NULL_ADDR) {
map->m_flags = 0;
} else {
- map->m_pa = blknr_to_addr(le32_to_cpu(*blkaddr));
+ map->m_pa = erofs_pos(le32_to_cpu(*blkaddr));
map->m_flags = EROFS_MAP_MAPPED;
}
goto out;
@@ -110,7 +110,7 @@ int erofs_map_blocks(struct erofs_inode *inode,
default:
map->m_deviceid = le16_to_cpu(idx->device_id) &
sbi.device_id_mask;
- map->m_pa = blknr_to_addr(le32_to_cpu(idx->blkaddr));
+ map->m_pa = erofs_pos(le32_to_cpu(idx->blkaddr));
map->m_flags = EROFS_MAP_MAPPED;
break;
}
@@ -119,23 +119,23 @@ out:
return err;
}
-int erofs_map_dev(struct erofs_sb_info *sbi, struct erofs_map_dev *map)
+int erofs_map_dev(struct erofs_map_dev *map)
{
struct erofs_device_info *dif;
int id;
if (map->m_deviceid) {
- if (sbi->extra_devices < map->m_deviceid)
+ if (sbi.extra_devices < map->m_deviceid)
return -ENODEV;
- } else if (sbi->extra_devices) {
- for (id = 0; id < sbi->extra_devices; ++id) {
+ } else if (sbi.extra_devices) {
+ for (id = 0; id < sbi.extra_devices; ++id) {
erofs_off_t startoff, length;
- dif = sbi->devs + id;
+ dif = sbi.devs + id;
if (!dif->mapped_blkaddr)
continue;
- startoff = blknr_to_addr(dif->mapped_blkaddr);
- length = blknr_to_addr(dif->blocks);
+ startoff = erofs_pos(dif->mapped_blkaddr);
+ length = erofs_pos(dif->blocks);
if (map->m_pa >= startoff &&
map->m_pa < startoff + length) {
@@ -147,19 +147,38 @@ int erofs_map_dev(struct erofs_sb_info *sbi, struct erofs_map_dev *map)
return 0;
}
+int erofs_read_one_data(struct erofs_map_blocks *map, char *buffer, u64 offset,
+ size_t len)
+{
+ struct erofs_map_dev mdev;
+ int ret;
+
+ mdev = (struct erofs_map_dev) {
+ .m_deviceid = map->m_deviceid,
+ .m_pa = map->m_pa,
+ };
+ ret = erofs_map_dev(&mdev);
+ if (ret)
+ return ret;
+
+ ret = erofs_dev_read(mdev.m_deviceid, buffer, mdev.m_pa + offset, len);
+ if (ret < 0)
+ return -EIO;
+ return 0;
+}
+
static int erofs_read_raw_data(struct erofs_inode *inode, char *buffer,
erofs_off_t size, erofs_off_t offset)
{
struct erofs_map_blocks map = {
.index = UINT_MAX,
};
- struct erofs_map_dev mdev;
int ret;
erofs_off_t ptr = offset;
while (ptr < offset + size) {
char *const estart = buffer + ptr - offset;
- erofs_off_t eend;
+ erofs_off_t eend, moff = 0;
map.m_la = ptr;
ret = erofs_map_blocks(inode, &map, 0);
@@ -168,14 +187,6 @@ static int erofs_read_raw_data(struct erofs_inode *inode, char *buffer,
DBG_BUGON(map.m_plen != map.m_llen);
- mdev = (struct erofs_map_dev) {
- .m_deviceid = map.m_deviceid,
- .m_pa = map.m_pa,
- };
- ret = erofs_map_dev(&sbi, &mdev);
- if (ret)
- return ret;
-
/* trim extent */
eend = min(offset + size, map.m_la + map.m_llen);
DBG_BUGON(ptr < map.m_la);
@@ -193,19 +204,73 @@ static int erofs_read_raw_data(struct erofs_inode *inode, char *buffer,
}
if (ptr > map.m_la) {
- mdev.m_pa += ptr - map.m_la;
+ moff = ptr - map.m_la;
map.m_la = ptr;
}
- ret = erofs_dev_read(mdev.m_deviceid, estart, mdev.m_pa,
- eend - map.m_la);
- if (ret < 0)
- return -EIO;
+ ret = erofs_read_one_data(&map, estart, moff, eend - map.m_la);
+ if (ret)
+ return ret;
ptr = eend;
}
return 0;
}
+int z_erofs_read_one_data(struct erofs_inode *inode,
+ struct erofs_map_blocks *map, char *raw, char *buffer,
+ erofs_off_t skip, erofs_off_t length, bool trimmed)
+{
+ struct erofs_map_dev mdev;
+ int ret = 0;
+
+ if (map->m_flags & EROFS_MAP_FRAGMENT) {
+ struct erofs_inode packed_inode = {
+ .nid = sbi.packed_nid,
+ };
+
+ ret = erofs_read_inode_from_disk(&packed_inode);
+ if (ret) {
+ erofs_err("failed to read packed inode from disk");
+ return ret;
+ }
+
+ return erofs_pread(&packed_inode, buffer, length - skip,
+ inode->fragmentoff + skip);
+ }
+
+ /* no device id here, thus it will always succeed */
+ mdev = (struct erofs_map_dev) {
+ .m_pa = map->m_pa,
+ };
+ ret = erofs_map_dev(&mdev);
+ if (ret) {
+ DBG_BUGON(1);
+ return ret;
+ }
+
+ ret = erofs_dev_read(mdev.m_deviceid, raw, mdev.m_pa, map->m_plen);
+ if (ret < 0)
+ return ret;
+
+ ret = z_erofs_decompress(&(struct z_erofs_decompress_req) {
+ .in = raw,
+ .out = buffer,
+ .decodedskip = skip,
+ .interlaced_offset =
+ map->m_algorithmformat == Z_EROFS_COMPRESSION_INTERLACED ?
+ erofs_blkoff(map->m_la) : 0,
+ .inputsize = map->m_plen,
+ .decodedlength = length,
+ .alg = map->m_algorithmformat,
+ .partial_decoding = trimmed ? true :
+ !(map->m_flags & EROFS_MAP_FULL_MAPPED) ||
+ (map->m_flags & EROFS_MAP_PARTIAL_REF),
+ });
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
erofs_off_t size, erofs_off_t offset)
{
@@ -213,8 +278,7 @@ static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
struct erofs_map_blocks map = {
.index = UINT_MAX,
};
- struct erofs_map_dev mdev;
- bool partial;
+ bool trimmed;
unsigned int bufsize = 0;
char *raw = NULL;
int ret = 0;
@@ -227,27 +291,17 @@ static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
if (ret)
break;
- /* no device id here, thus it will always succeed */
- mdev = (struct erofs_map_dev) {
- .m_pa = map.m_pa,
- };
- ret = erofs_map_dev(&sbi, &mdev);
- if (ret) {
- DBG_BUGON(1);
- break;
- }
-
/*
* trim to the needed size if the returned extent is quite
* larger than requested, and set up partial flag as well.
*/
if (end < map.m_la + map.m_llen) {
length = end - map.m_la;
- partial = true;
+ trimmed = true;
} else {
DBG_BUGON(end != map.m_la + map.m_llen);
length = map.m_llen;
- partial = !(map.m_flags & EROFS_MAP_FULL_MAPPED);
+ trimmed = false;
}
if (map.m_la < offset) {
@@ -272,19 +326,10 @@ static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
break;
}
}
- ret = erofs_dev_read(mdev.m_deviceid, raw, mdev.m_pa, map.m_plen);
- if (ret < 0)
- break;
- ret = z_erofs_decompress(&(struct z_erofs_decompress_req) {
- .in = raw,
- .out = buffer + end - offset,
- .decodedskip = skip,
- .inputsize = map.m_plen,
- .decodedlength = length,
- .alg = map.m_algorithmformat,
- .partial_decoding = partial
- });
+ ret = z_erofs_read_one_data(inode, &map, raw,
+ buffer + end - offset, skip, length,
+ trimmed);
if (ret < 0)
break;
}
@@ -301,8 +346,8 @@ int erofs_pread(struct erofs_inode *inode, char *buf,
case EROFS_INODE_FLAT_INLINE:
case EROFS_INODE_CHUNK_BASED:
return erofs_read_raw_data(inode, buf, count, offset);
- case EROFS_INODE_FLAT_COMPRESSION_LEGACY:
- case EROFS_INODE_FLAT_COMPRESSION:
+ case EROFS_INODE_COMPRESSED_FULL:
+ case EROFS_INODE_COMPRESSED_COMPACT:
return z_erofs_read_data(inode, buf, count, offset);
default:
break;