9 Commits
Author SHA1 Message Date
Ernesto A. Fernández af0671f58f 0.3.4
This release fixes the build for the Linux 6.5 release candidate, and it
should also fix a long-standing mmap bug in Linux 5.14 and above, which
resulted in null pointer dereference.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-08-14 23:50:19 -03:00
Ernesto A. Fernández 740d7db44b Fix mmap for kernels 5.14 and above
Adamantinum reports trouble while attempting to use apfs as root with
kernel version 6.4.8:

  https://github.com/linux-apfs/linux-apfs-rw/issues/50

His callstack shows an npd in mmap, and the bug can be easily reproduced
by generic/029, but not in kernel version 5.3, where I usually run my
tests. Via bisection I tracked the problem back to kernel commit
0af573780b0b ("mm: require ->set_page_dirty to be explicitly wired up").
Starting with kernel 5.14, the ->set_page_dirty() address space method
gets called unconditionally, without checking for null first, so set it
explicitly.

Kernel 5.18 replaced ->set_page_dirty() with ->dirty_folio(), so take
care of that as well.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-08-14 19:50:22 -03:00
Ernesto A. Fernández bc2be91bb4 Update FMODE_* stuff for the 6.5 kernel
The build is broken for the Linux 6.5 release candidate:

  https://github.com/linux-apfs/linux-apfs-rw/issues/49

The fmode_t type can no longer be used for block open flags, and we must
switch to blk_mode_t instead. FMODE_EXCL is no longer needed, since just
having a holder already implies it, but this requires a change to the
blkdev_put() api. There is also a new argument to blkdev_get_by_path()
which can be safely ignored. And finally, sb->s_mode is gone, but it
doesn't seem to make any difference.

Update all the code to the new apis.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-08-08 20:58:11 -03:00
Ernesto A. Fernández 5c4773c036 Convert all strcpy() to strscpy()
The build appears to be broken for some configurations on the Linux 6.5
release candidate:

  https://github.com/linux-apfs/linux-apfs-rw/issues/49

Some sort of static overflow check is going on for strcpy(), and several
of my calls are triggering false positives. Just be safe and switch them
all to strscpy(). The qstr stuff in particular looked a bit too weird
anyway.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-08-08 19:14:10 -03:00
Ernesto A. Fernández 1e3fa28c4f Increase limit for compressed size of files
A recent cryptex image contains a file with a compressed size above the
current 128Mb limit. This limit exists because of vmalloc(), and it can
be increased (at least for 64-bit archs). Do that for now, but I really
should try to restructure the code to use smaller kmalloc() allocations
instead.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-06-07 20:40:53 -03:00
Ernesto A. Fernández d4799475db 0.3.3
This release fixes builds for kernels below 5.3, which have all been
broken for a while. So far development has only been focused on Linux
5.3, and I've relied on reporters to find out about issues on other
versions. From now on I will at least run builds for a range of kernels
before each release, so hopefully this won't happen again.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-05-24 23:05:25 -03:00
Ernesto A. Fernández c54e3a7423 Silence b_blocknr width warnings on old kernels
While fixing the build for old kernels, I noticed that in some of them
you get a warning when you prink a buffer head's b_blocknr as 0x%llx.
I haven't checked but I'm guessing that, at some point, sector_t on
64-bit archs went from being 'unsigned long' to 'unsigned long long',
so these warnings went away. Anyway, just cast b_blocknr before printing
it in all kernels.

At some point I do need to attempt a 32-bit build though. It's been a
long time, and I'm sure there will be much worse problems than this one.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-05-19 18:49:38 -03:00
Ernesto A. Fernández b185ce13bd Fix builds before generic_copy_file_range()
Even with the previous patch, builds are still broken for all kernels
before 5.2. The problem is that generic_copy_file_range() wasn't
introduced until Linux 5.3. The good news is that we don't actually need
it for older kernels: as I explained in the commit message for the patch
that broke this, we can just returning an error code from
apfs_remap_file_range().

Fixes: 396650b056 ("Don't let copy_file_range() use clones")
Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-05-19 18:31:51 -03:00
Ernesto A. Fernández 42d70a8997 Fix build for kernels with separate clone/dedupe
Luflosi reports that the build is broken once again for Linux 4.14 and
4.19:

  https://github.com/linux-apfs/linux-apfs-rw/issues/43

The problem here is that the clone and dedupe operations used to be
implemented with separate methods. Since Linux 4.20, they both use the
same ->remap_file_range(), with some flags passed to tell them apart.
So when I implemented clones I used this new version and broke the build
for the old ones. Fix it now.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-05-19 18:31:51 -03:00
12 changed files with 68 additions and 16 deletions

No files matched your search

+4
View File
@@ -885,7 +885,11 @@ extern int apfs_get_new_block(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create);
extern int APFS_GET_NEW_BLOCK_MAXOPS(void);
extern int apfs_truncate(struct apfs_dstream_info *dstream, loff_t new_size);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
extern loff_t apfs_remap_file_range(struct file *src_file, loff_t off, struct file *dst_file, loff_t destoff, loff_t len, unsigned int remap_flags);
#else
extern int apfs_clone_file_range(struct file *src_file, loff_t off, struct file *dst_file, loff_t destoff, u64 len);
#endif
extern int apfs_clone_extents(struct apfs_dstream_info *dstream, u64 new_id);
/* file.c */
+5 -2
View File
@@ -36,8 +36,11 @@ static inline void *kvmalloc(size_t size, gfp_t flags)
#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) */
/* maximum size of compressed data currently supported */
#define MAX_FBUF_SIZE (128 * 1024 * 1024)
/*
* Maximum size of compressed data currently supported. This is getting too
* big already. TODO: avoid vmalloc().
*/
#define MAX_FBUF_SIZE (256 * 1024 * 1024)
struct apfs_compress_file_data {
struct apfs_compress_hdr hdr;
+4 -4
View File
@@ -268,7 +268,7 @@ static int apfs_build_dentry_unhashed_key(struct qstr *qname, u64 parent_id,
apfs_key_set_hdr(APFS_TYPE_DIR_REC, parent_id, key);
key->name_len = cpu_to_le16(namelen);
strcpy(key->name, qname->name);
strscpy(key->name, qname->name, namelen);
*key_p = key;
return key_len;
@@ -297,7 +297,7 @@ static int apfs_build_dentry_hashed_key(struct qstr *qname, u64 hash, u64 parent
apfs_key_set_hdr(APFS_TYPE_DIR_REC, parent_id, key);
key->name_len_and_hash = cpu_to_le32(namelen | hash);
strcpy(key->name, qname->name);
strscpy(key->name, qname->name, namelen);
*key_p = key;
return key_len;
@@ -434,7 +434,7 @@ static int apfs_build_sibling_val(struct dentry *dentry,
val->parent_id = cpu_to_le64(apfs_ino(parent));
val->name_len = cpu_to_le16(namelen);
strcpy(val->name, qname->name);
strscpy(val->name, qname->name, namelen);
*val_p = val;
return val_len;
@@ -1089,7 +1089,7 @@ static int apfs_sibling_link_from_query(struct apfs_query *query,
*name = kmalloc(namelen, GFP_KERNEL);
if (!*name)
return -ENOMEM;
strcpy(*name, siblink->name);
strscpy(*name, siblink->name, namelen);
*parent = le64_to_cpu(siblink->parent_id);
return 0;
}
+1 -1
View File
@@ -1,5 +1,5 @@
PACKAGE_NAME="linux-apfs-rw"
PACKAGE_VERSION="0.3.2"
PACKAGE_VERSION="0.3.4"
BUILT_MODULE_NAME[0]="apfs"
DEST_MODULE_LOCATION[0]="/extra"
+6
View File
@@ -1636,7 +1636,11 @@ int apfs_truncate(struct apfs_dstream_info *dstream, loff_t new_size)
return apfs_create_hole(dstream, old_blks, new_blks);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
loff_t apfs_remap_file_range(struct file *src_file, loff_t off, struct file *dst_file, loff_t destoff, loff_t len, unsigned int remap_flags)
#else
int apfs_clone_file_range(struct file *src_file, loff_t off, struct file *dst_file, loff_t destoff, u64 len)
#endif
{
struct inode *src_inode = file_inode(src_file);
struct inode *dst_inode = file_inode(dst_file);
@@ -1651,8 +1655,10 @@ loff_t apfs_remap_file_range(struct file *src_file, loff_t off, struct file *dst
const u64 xfield_flags = APFS_INODE_MAINTAIN_DIR_STATS | APFS_INODE_IS_SPARSE | APFS_INODE_HAS_PURGEABLE_FLAGS;
int err;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
if (remap_flags & ~(REMAP_FILE_ADVISORY))
return -EINVAL;
#endif
if (src_inode == dst_inode)
return -EINVAL;
+8
View File
@@ -145,8 +145,16 @@ const struct file_operations apfs_file_operations = {
.open = generic_file_open,
.fsync = apfs_fsync,
.unlocked_ioctl = apfs_file_ioctl,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
.copy_file_range = generic_copy_file_range,
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
.remap_file_range = apfs_remap_file_range,
#else
.clone_file_range = apfs_clone_file_range,
#endif
};
#if LINUX_VERSION_CODE == KERNEL_VERSION(5, 3, 0)
+6
View File
@@ -679,6 +679,12 @@ static void apfs_noop_invalidate_folio(struct folio *folio, size_t offset, size_
/* bmap is not implemented to avoid issues with CoW on swapfiles */
static const struct address_space_operations apfs_aops = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
.dirty_folio = block_dirty_folio,
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0)
.set_page_dirty = __set_page_dirty_buffers,
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)
.read_folio = apfs_read_folio,
#else
+2 -2
View File
@@ -138,12 +138,12 @@ struct apfs_node *apfs_read_node(struct super_block *sb, u64 oid, u32 storage,
if (nxi->nx_flags & APFS_CHECK_NODES && !apfs_obj_verify_csum(sb, bh)) {
/* TODO: don't check this twice for virtual/physical objects */
apfs_err(sb, "bad checksum for node in block 0x%llx", bh->b_blocknr);
apfs_err(sb, "bad checksum for node in block 0x%llx", (unsigned long long)bh->b_blocknr);
apfs_node_free(node);
return ERR_PTR(-EFSBADCRC);
}
if (!apfs_node_is_valid(sb, node)) {
apfs_err(sb, "bad node in block 0x%llx", bh->b_blocknr);
apfs_err(sb, "bad node in block 0x%llx", (unsigned long long)bh->b_blocknr);
apfs_node_free(node);
return ERR_PTR(-EFSCORRUPTED);
}
+1 -1
View File
@@ -383,7 +383,7 @@ struct buffer_head *apfs_read_object_block(struct super_block *sb, u64 bno, bool
if (!preserve) {
err = apfs_free_queue_insert(sb, bh->b_blocknr, 1);
if (err)
apfs_err(sb, "free queue insertion failed for 0x%llx", bh->b_blocknr);
apfs_err(sb, "free queue insertion failed for 0x%llx", (unsigned long long)bh->b_blocknr);
} else if ((type & APFS_OBJECT_TYPE_MASK) != APFS_OBJECT_TYPE_FS) {
vsb_raw = APFS_SB(sb)->s_vsb_raw;
apfs_assert_in_transaction(sb, &vsb_raw->apfs_o);
+2 -2
View File
@@ -116,7 +116,7 @@ static int apfs_create_snap_metadata_rec(struct inode *mntpoint, struct apfs_nod
raw_val->extentref_tree_type = vsb_raw->apfs_extentref_tree_type;
raw_val->flags = 0;
raw_val->name_len = cpu_to_le16(name_len + 1); /* Count the null byte */
strcpy(raw_val->name, name);
strscpy(raw_val->name, name, name_len + 1);
apfs_assert_in_transaction(sb, &vsb_raw->apfs_o);
raw_val->inum = vsb_raw->apfs_next_obj_id;
@@ -173,7 +173,7 @@ static int apfs_create_snap_name_rec(struct apfs_node *snap_root, const char *na
}
apfs_key_set_hdr(APFS_TYPE_SNAP_NAME, APFS_SNAP_NAME_OBJ_ID, raw_key);
raw_key->name_len = cpu_to_le16(name_len + 1); /* Count the null byte */
strcpy(raw_key->name, name);
strscpy(raw_key->name, name, name_len + 1);
raw_val.snap_xid = cpu_to_le64(APFS_NXI(sb)->nx_xid);
+28 -3
View File
@@ -272,11 +272,13 @@ static void apfs_update_software_info(struct super_block *sb)
memmove(mod_by + 1, mod_by, (APFS_MAX_HIST - 1) * sizeof(*mod_by));
memset(mod_by->id, 0, sizeof(mod_by->id));
strcpy(mod_by->id, APFS_MODULE_ID_STRING);
strscpy(mod_by->id, APFS_MODULE_ID_STRING, sizeof(mod_by->id));
mod_by->timestamp = cpu_to_le64(ktime_get_real_ns());
mod_by->last_xid = cpu_to_le64(APFS_NXI(sb)->nx_xid);
}
static struct file_system_type apfs_fs_type;
/**
* apfs_unmap_main_super - Clean up apfs_map_main_super()
* @sbi: in-memory superblock info
@@ -286,11 +288,15 @@ static void apfs_update_software_info(struct super_block *sb)
static inline void apfs_unmap_main_super(struct apfs_sb_info *sbi)
{
struct apfs_nxsb_info *nxi = sbi->s_nxi;
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0)
fmode_t mode = FMODE_READ | FMODE_EXCL;
#endif
struct apfs_object *obj = NULL;
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0)
if (nxi->nx_flags & APFS_READWRITE)
mode |= FMODE_WRITE;
#endif
lockdep_assert_held(&nxs_mutex);
@@ -304,7 +310,12 @@ static inline void apfs_unmap_main_super(struct apfs_sb_info *sbi)
obj->o_bh = NULL;
obj = NULL;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
blkdev_put(nxi->nx_bdev, &apfs_fs_type);
#else
blkdev_put(nxi->nx_bdev, mode);
#endif
list_del(&nxi->nx_list);
kfree(nxi);
out:
@@ -1450,8 +1461,6 @@ static int apfs_set_super(struct super_block *sb, void *data)
return err;
}
static struct file_system_type apfs_fs_type;
/*
* Wrapper for lookup_bdev() that supports older kernels.
*/
@@ -1480,7 +1489,11 @@ static int apfs_lookup_bdev(const char *pathname, dev_t *dev)
*
* Returns 0 on success, or a negative error code in case of failure.
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
static int apfs_attach_nxi(struct apfs_sb_info *sbi, const char *dev_name, blk_mode_t mode)
#else
static int apfs_attach_nxi(struct apfs_sb_info *sbi, const char *dev_name, fmode_t mode)
#endif
{
struct apfs_nxsb_info *nxi;
dev_t dev = 0;
@@ -1500,7 +1513,11 @@ static int apfs_attach_nxi(struct apfs_sb_info *sbi, const char *dev_name, fmode
if (!nxi)
return -ENOMEM;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
bdev = blkdev_get_by_path(dev_name, mode, &apfs_fs_type, NULL);
#else
bdev = blkdev_get_by_path(dev_name, mode, &apfs_fs_type);
#endif
if (IS_ERR(bdev)) {
kfree(nxi);
return PTR_ERR(bdev);
@@ -1527,7 +1544,11 @@ static struct dentry *apfs_mount(struct file_system_type *fs_type, int flags,
struct apfs_nxsb_info *nxi;
struct super_block *sb;
struct apfs_sb_info *sbi;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
blk_mode_t mode = sb_open_mode(flags);
#else
fmode_t mode = FMODE_READ | FMODE_EXCL;
#endif
int error = 0;
mutex_lock(&nxs_mutex);
@@ -1544,8 +1565,10 @@ static struct dentry *apfs_mount(struct file_system_type *fs_type, int flags,
if (sbi->s_snap_name)
flags |= SB_RDONLY;
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0)
if (!(flags & SB_RDONLY))
mode |= FMODE_WRITE;
#endif
error = apfs_attach_nxi(sbi, dev_name, mode);
if (error)
@@ -1572,7 +1595,9 @@ static struct dentry *apfs_mount(struct file_system_type *fs_type, int flags,
error = apfs_map_main_super(sb);
if (error)
goto out_deactivate_super;
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0)
sb->s_mode = mode;
#endif
snprintf(sb->s_id, sizeof(sb->s_id), "%xg", sb->s_dev);
error = apfs_fill_super(sb, data, flags & SB_SILENT ? 1 : 0);
if (error)
+1 -1
View File
@@ -465,7 +465,7 @@ static int apfs_build_xattr_key(const char *name, u64 ino, struct apfs_xattr_key
apfs_key_set_hdr(APFS_TYPE_XATTR, ino, key);
key->name_len = cpu_to_le16(namelen);
strcpy(key->name, name);
strscpy(key->name, name, namelen);
*key_p = key;
return key_len;