30 Commits
Author SHA1 Message Date
Ernesto A. Fernández 97ba9b0dc8 0.3.2
This release improves error messages, which will hopefully make it
easier to communicate with bug reporters. A number of bugs are also
addressed, most importantly a missing check on the node count limit for
the free queues, which could trigger reports of corruption in some rare
cases (no data loss though, as far as I can see).

Support is added for the 6.3 kernel version, courtesy of Woody Suwalski.

I was hoping to wait until encryption was ready before making a release,
but I'm not finding much time for apfs these days so that could take a
while. In the meantime, it's better to get this stuff out the door.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-05-16 17:57:01 -03:00
Ernesto A. Fernández 8b99af84d1 Try to avoid failing insertion on fragmented nodes
I'm getting somewhat regular failures on generic/112 because the ip free
queue for the test image has a one-node limit, and it sometimes gets
full. The thing is, the node actually has plenty of room, but it's all
in the key free list, so I can't allocate values. From now on, if an
insertion fails with ENOSPC, check the available room and defragment
the node if it should be enough. Then retry the insertion instead of
jumping straight to a split.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-04-17 20:05:26 -03:00
Ernesto A. Fernández 3a605ec8ff Don't ignore error code in apfs_flush_fq_rec()
The previous patch was intended to trigger an immediate abort when a
free queue violated its own node limit. But I managed to trigger this
event again with generic/112, and to my surprise there were several
operations in dmesg before the abort finally happened. The problem here
is in apfs_flush_fq_rec(): when apfs_main_free() fails (in this case
because the ip free queue is full), the error code is reported but not
bubbled up. This is a mistake of course, so fix it.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-04-12 17:49:12 -03:00
Ernesto A. Fernández ccefdd13d8 Don't allow free queues to exceed their node limit
Free queues have a node limit that is set by the mkfs. Very rarely,
generic/112 manages to break this limit, and corruption is reported.
Preventing corruption is always the number one priority, so add a check
for this limit after every free queue insertion so that we can abort
immediately when it fails.

Remember though, that there is a deeper issue here: either the btrees
are becoming too fragmented, or the flushes are not frequent enough. The
queues should never fill like this.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-04-12 16:47:55 -03:00
Ernesto A. Fernández 5beaaae75c Fix out-of-bounds read on snapshot ioctl
Don't call strlen() on the name argument of the ioctl, because it may
not be null-terminated. I'm not sure what the consequences of this are,
but I don't think it should be exploitable. Use strnlen() instead.

Also move the length check outside the transaction: we don't want simple
user mistakes triggering aborts. And fix the returned error code: there
is no on-disk corruption going on, just user error.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-04-05 23:06:22 -03:00
Ernesto A. Fernández 0f47fc762a Fix memory leak on xattr length overflow
Always free the allocated dstream in apfs_xattr_extents_read() before
returning.

Fixes: 362c4e32ab ("Allow internal reads of huge xattrs")
Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-04-05 22:21:53 -03:00
Ernesto A. Fernández d4797823ea Improve error reporting
The driver is much closer to being usable, so I might start getting
subtler bug reports soon. To make them easier to handle, put error
messages all over the place. I should have done this from the beginning,
but I guess I didn't fully understand the need back then.

From now my general policy will be to use apfs_warn() for user errors or
unsupported features; apfs_err() for things that are probably corruption
or io errors; and apfs_alert() for things that are most likely bugs.
These last two should be rare, so the same error/alert will be thrown by
several layers in the callstack to provide as much information as
possible. Be careful and don't flood the console on normal situations.

Also, make messages with a log level lower than warning output their
function name and line number, which I think will help debugging more
than the actual messages.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-04-05 21:59:24 -03:00
Woody Suwalski 24daddc890 Changes needed to build on kernel 6.3 2023-03-24 15:12:47 -03:00
Ernesto A. Fernández 72e8873a6a 0.3.1
Snapshots and clones seem to be working properly now, though testing has
been somewhat limited. As far as I know (I could definitely be wrong),
this is the last thing that was needed to fully support writes to
unencrypted volumes, so it's an important change that warrants a new
release. Everything remains experimental, of course.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-03-23 21:15:51 -03:00
Ernesto A. Fernández 8bb6d325ea Try to make longer free queue records
When running xfstests generic/142 (a file clone test), I encounter the
following error:

  APFS (2cg): creation of cpm blocks not yet supported

This is expected to happen when we have an absurdly large number of free
queue records. In this particular test, many writes are made to the
middle of files, so each of these writes becomes its own extent. When
the file is later deleted, all of these extents are deleted in a single
massive transaction. Each of them becomes its own free queue record, and
there is no opportunity to flush the queue.

There are three problems in here. The first one is that writes to the
middle of files should be allowed to allocate longer extents. The second
one is that truncation of orphan files should get split in multiple
transactions. These problems are important, but for now I just want to
test file clones, so I'll leave the fixes for later.

The third problem, much easier to fix, is that the driver should never
create bursts of small consecutive free queue records: it should just
merge them all into a bigger one. For that purpose, keep a cache of the
latest freed range, and always attempt to extend it instead of creating
a new record directly.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-03-23 00:50:22 -03:00
Ernesto A. Fernández 7ea6920bc4 Give a clone its own dstream before mmap writes
Call apfs_inode_create_exclusive_dstream() during mkwrite(), just like
we do during write_begin(), or else things could get weird for cloned
files. This patch fixes generic/140 of xfstests.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-03-22 19:18:34 -03:00
Ernesto A. Fernández 699a07189a Don't try to map invalid blocks in get_block()
If __apfs_get_block() gets called with a block number after the end of
the file, it still maps the buffer head successfully but using invalid
information from the previous extent. So far this has never been a
problem for any of the callers, but the previous patch implemented
FIEMAP and generic_block_fiemap() appears to use an unmapped buffer head
to check for EOF. With the current code, it just loops forever.

So, do the right thing and check that the logical block is in range
before doing any extent lookups. At this point there is no reason why
apfs_extent_read() should ever fail to find a matching extent, so make
it return EFSCORRUPTED when that happens.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-03-22 18:30:18 -03:00
Ernesto A. Fernández 1490bc7d36 Support FIEMAP for the 5.3 kernel
I'm trying to run the "clone" group of xfstests, but it seems that those
tests require the FIEMAP ioctl to confirm that clones share the same
physical extents. This doesn't seem trivial to implement for all kernel
versions, so for now only do it for 5.3, the one I use for testing.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-03-22 16:14:02 -03:00
Ernesto A. Fernández d74165587b Don't free the blocks on update extent deletion
If an UPDATE record is created to take a reference, and then a reference
to that same range gets put, then the UPDATE extent will simply get
deleted. When that happens, we mistakenly mark all of its blocks as
free, but the blocks are still in use by the original snapshot. Fix
this.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-03-20 21:29:59 -03:00
Ernesto A. Fernández e8847c734a Don't copy the sparse flag into cloned inodes
When an inode becomes a clone, the internal flags from the original get
copied to it. The problem is that a few of these flags imply the
presence of a matching xfield, but those haven't been created at this
point. The sparse xfield, in particular, may never get created at all if
the original file is no longer sparse. Don't copy those xfield flags,
just set them when the xfield gets created as usual.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-03-17 21:32:10 -03:00
Ernesto A. Fernández 201b0872fd Correct return value for apfs_remap_file_range()
The return value for apfs_remap_file_range() on sucess is supposed to be
the length of the copied region, not 0. I haven't checked if this has
any practical consequences, but fix it.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-03-17 17:20:49 -03:00
Ernesto A. Fernández 396650b056 Don't let copy_file_range() use clones
I'm seeing a silly regression in generic/430: now that clones are
implemented, copy_file_range() has started to use them under the hood,
but it only wants to clone a portion of the file, and that's not
something I'm interested in supporting.

To avoid this on newer kernels, set ->copy_file_range() to the generic
implementation. For older kernels, start throwing errors if anything
calls apfs_remap_file_range() for partial ranges. This is the right
thing to do in general, since we don't support that.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-03-17 17:20:49 -03:00
Ernesto A. Fernández b00b571757 Implement writes to cloned extents
This rewrites a considerable part of the physical extents code, so let's
hope for the best.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-03-17 01:42:44 -03:00
Ernesto A. Fernández c994a77bfd Fork cloned dstreams on write operations
When first created, a clone is just a new reference to the original
dstream. At this point the driver can't even delete a cloned file,
because all of the dstream extents go away, and those are still in use
by the other copies.

So, before any write operation happens on a clone file, create a new
dstream for the modified copy, with its own file extents and new
references to the physical extents. This should at least allow deletion
to work correctly, but real writes will not, because individual extent
changes haven't been implemented yet. It won't be hard though, because
now my mind is a little more clear about this stuff.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-03-17 01:42:44 -03:00
Ernesto A. Fernández 573fe59e7f Implement basic file cloning functionality
Implement the FICLONE ioctl to be able to create clones of files. This
will never work the same as macos, because Linux requires that the user
first creates the target file, and only then it can be made into a
clone. A crash halfway throught would leave an empty file in place of
the clone.

This patch only duplicates the data stream, it does not deal with the
actual extents. Reads from clones should work correctly, but writes will
not. I'll get to that right away.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-03-08 22:22:18 -03:00
Ernesto A. Fernández 76babcc7ad Fix cleanup on failure during snapshot creation
It seems that I messed up the cleanups during some late fixups to the
code in apfs_do_ioc_take_snapshot(): the transaction could be left open
in some cases. Fix it.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-03-08 19:18:50 -03:00
Ernesto A. Fernández 9c39466269 Make updates to blocks_freed and blocks_alloced
The apfs_total_blocks_freed and apfs_total_blocks_alloced fields of the
volume superblock keep track of the total number of blocks that have
been freed/alloced through the whole lifetime of the volume so far.
Since the value of these fields depends on historical operations, it
can't be calculated from the volume contents, so I figured I could just
ignore them without consequences.

This seems to be mostly correct, but the official fsck does complain if
the freed blocks appear to be more than the alloced blocks. It's just a
warning, not an error, but it does often trigger after the official
driver makes changes to one of my images. So, try to be careful with
these two fields from now on.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-03-06 22:05:29 -03:00
Ernesto A. Fernández 7a5d4085ea Fix apfs_node_min_table_size() for non-leaf nodes
In the previous patch I forgot to consider that index nodes have
different value sizes than leaf nodes, even for fixed key/value size
trees. Fix it.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-02-22 17:30:45 -03:00
Ernesto A. Fernández d29943de45 Fix apfs_node_min_table_size() for new tree types
When I started working with OMAP_SNAPSHOT trees, I forgot to add support
for them inside apfs_node_min_table_size(). The result is that the index
of each of those trees is initialized to a tiny minimum size, as if the
keys and values were of variable length. This went unnoticed for a while
because of other bugs in my fsck, which I've recently fixed in
5a20a212d8f0 ("apfsck: fix checks of index size for fixed kv size"). The
official fsck always complained though, I just wasn't using it:

  ** Checking the snapshot metadata.
  error: btn: invalid btn_table_space (0, 64), given btn_flags (0x7)

So, implement apfs_node_min_table_size() properly for the OMAP_SNAPSHOT
tree type. For consistency with the fsck, also implement the FEXT tree
type, but I don't actually support writes to sealed volumes in the
driver at the moment.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-02-21 22:55:31 -03:00
Ernesto A. Fernández 2e63fbc8dd Add APFS prefix to all object type macros
Some recently defined object types are missing the "APFS_" prefix. I've
fixed this a while back in the fsck, do it in the driver too.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-02-21 22:54:39 -03:00
Ernesto A. Fernández 15d5ab65d5 Set a snapshot's inum on creation
Right now the "inum" field of new snapshot metadata record gets set to
zero, and the official fsck is complaining about it. I was confused for
a while about this field because I thought it referred to an actual file
somewhere, but now I'm pretty sure it's just an identifier for the
snapshot, which gets picked from the same pool as regular inode numbers.
So, do that.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-02-21 20:16:18 -03:00
Ernesto A. Fernández 8c330866de Fix volume block count after snapshots
There are two bugs in the way apfs_fs_alloc_count is calculated after a
snapshot. I had not noticed them so far because it randomly gets the
right number as long as no node splits happen, but the new test I'm
working on (called apfs/006 for now) does split nodes and reports the
corruption.

Anyway, the problems are that the volume superblock should never get
counted in apfs_fs_alloc_count, not even after a snapshot; and that
regular CoW of virtual nodes needs to increase apfs_fs_alloc_count if
the original is preserved in a snapshot. Fix both now.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-02-17 22:53:18 -03:00
Ernesto A. Fernández 0e15c381c1 Fix mmap regressions from snapshot writes
The previous commit has caused regressions for a couple of mmap tests
from xfstests. I always assumed that all buffer heads mapping a physical
block would be the same object, with the same metadata, but that's only
true for buffer heads in the same mapping. So the apfs_sb_bread() inside
apfs_zero_dstream_tail() will not get the same bh that joined the
transaction via mmap, and two separate CoWs will happen, losing some of
the writes.

So, first of all, apfs_zero_dstream_tail() can't just read blocks from
the device like it used to, it needs to have a reference to the file
mapping and work with that. Put a pointer to the inode in the dstream
info for this purpose. Now, since individual buffer tails are already
zeroed during ->write_begin(), spin the part of that code that doesn't
deal with transactions into __apfs_write_begin() and __apfs_write_end(),
and use those here. Finally, reorder apfs_dstream_get_new_block() to
avoid problems with the extent cache during this recursion.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-02-16 18:47:28 -03:00
Ernesto A. Fernández 9e5e2aaf17 Implement writes to snapshot files
Make the necessary changes to the extent code so that writes to files
that are part of a snapshot will work correctly. I don't believe there
is any real risk of deleting the original content at this point, because
those physical extents are kept in a separate tree. The real problem is
keeping track of extent references, because that will become important
for the official implementation if a snapshot gets deleted.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-02-16 18:47:28 -03:00
Ernesto A. Fernández b5a2f9fb62 Update the README with compression support
As I mentioned in the release notes, the driver now has a reasonable
support for compression. The README still claims that it doesn't,
though, because I forgot to update it before the release. Fix it now.

Signed-off-by: Ernesto A. Fernández <ernesto@corellium.com>
2023-02-02 19:19:00 -03:00
21 changed files with 2634 additions and 810 deletions

No files matched your search

+1 -2
View File
@@ -22,8 +22,7 @@ This module is the result of reverse engineering and testing has been limited.
If you make use of the write support, expect data corruption. Please report any
issues that you find, but I can't promise a quick resolution at this stage.
Many features are not yet implemented, including encryption and most forms of
compression.
Encryption is not yet implemented even in read-only mode.
Build
=====
+56 -16
View File
@@ -133,6 +133,13 @@ struct apfs_spaceman {
u32 sm_cib_count; /* Number of chunk-info blocks */
u64 sm_free_count; /* Number of free blocks */
u32 sm_addr_offset; /* Offset of cib addresses in @sm_raw */
/*
* A range of freed blocks not yet put in the free queue. Extend this as
* much as possible before creating an actual record.
*/
u64 sm_free_cache_base;
u64 sm_free_cache_blkcnt;
};
#define TRANSACTION_MAIN_QUEUE_MAX 4096
@@ -490,7 +497,7 @@ static inline void apfs_init_sibling_link_key(u64 ino, u64 id,
{
key->id = ino;
key->type = APFS_TYPE_SIBLING_LINK;
key->number = id; /* Only guessing */
key->number = id; /* Only guessing (TODO) */
key->name = NULL;
}
@@ -660,6 +667,7 @@ struct apfs_phys_extent {
u64 blkcount;
u64 len; /* In bytes */
u32 refcnt;
u8 kind;
};
/*
@@ -667,12 +675,14 @@ struct apfs_phys_extent {
*/
struct apfs_dstream_info {
struct super_block *ds_sb; /* Filesystem superblock */
struct inode *ds_inode; /* NULL for xattr dstreams */
u64 ds_id; /* ID of the extent records */
u64 ds_size; /* Length of the stream */
u64 ds_sparse_bytes;/* Hole byte count in stream */
struct apfs_file_extent ds_cached_ext; /* Latest extent record */
bool ds_ext_dirty; /* Is ds_cached_ext dirty? */
spinlock_t ds_ext_lock; /* Protects ds_cached_ext */
bool ds_shared; /* Has multiple references? */
};
/**
@@ -764,17 +774,22 @@ struct apfs_xattr {
bool has_dstream;
};
#define apfs_emerg(sb, fmt, ...) apfs_msg(sb, KERN_EMERG, fmt, ##__VA_ARGS__)
#define apfs_alert(sb, fmt, ...) apfs_msg(sb, KERN_ALERT, fmt, ##__VA_ARGS__)
#define apfs_crit(sb, fmt, ...) apfs_msg(sb, KERN_CRIT, fmt, ##__VA_ARGS__)
#define apfs_err(sb, fmt, ...) apfs_msg(sb, KERN_ERR, fmt, ##__VA_ARGS__)
#define apfs_warn(sb, fmt, ...) apfs_msg(sb, KERN_WARNING, fmt, ##__VA_ARGS__)
#define apfs_notice(sb, fmt, ...) apfs_msg(sb, KERN_NOTICE, fmt, ##__VA_ARGS__)
#define apfs_info(sb, fmt, ...) apfs_msg(sb, KERN_INFO, fmt, ##__VA_ARGS__)
/*
* Report function name and line number for the message types that are likely
* to signal a bug, to make things easier for reporters. Don't do this for the
* common messages, there is no point and it makes the console look too busy.
*/
#define apfs_emerg(sb, fmt, ...) apfs_msg(sb, KERN_EMERG, __func__, __LINE__, fmt, ##__VA_ARGS__)
#define apfs_alert(sb, fmt, ...) apfs_msg(sb, KERN_ALERT, __func__, __LINE__, fmt, ##__VA_ARGS__)
#define apfs_crit(sb, fmt, ...) apfs_msg(sb, KERN_CRIT, __func__, __LINE__, fmt, ##__VA_ARGS__)
#define apfs_err(sb, fmt, ...) apfs_msg(sb, KERN_ERR, __func__, __LINE__, fmt, ##__VA_ARGS__)
#define apfs_warn(sb, fmt, ...) apfs_msg(sb, KERN_WARNING, NULL, 0, fmt, ##__VA_ARGS__)
#define apfs_notice(sb, fmt, ...) apfs_msg(sb, KERN_NOTICE, NULL, 0, fmt, ##__VA_ARGS__)
#define apfs_info(sb, fmt, ...) apfs_msg(sb, KERN_INFO, NULL, 0, fmt, ##__VA_ARGS__)
#ifdef CONFIG_APFS_DEBUG
#define ASSERT(expr) BUG_ON(!(expr))
#define apfs_debug(sb, fmt, ...) apfs_msg(sb, KERN_DEBUG, fmt, ##__VA_ARGS__)
#define apfs_debug(sb, fmt, ...) apfs_msg(sb, KERN_DEBUG, __func__, __LINE__, fmt, ##__VA_ARGS__)
#else
#define ASSERT(expr) ((void)0)
#define apfs_debug(sb, fmt, ...) no_printk(fmt, ##__VA_ARGS__)
@@ -797,7 +812,6 @@ extern struct apfs_query *apfs_alloc_query(struct apfs_node *node,
struct apfs_query *parent);
extern void apfs_free_query(struct apfs_query *query);
extern int apfs_btree_query(struct super_block *sb, struct apfs_query **query);
extern struct apfs_node *apfs_omap_read_node(struct super_block *sb, u64 id);
extern int apfs_omap_lookup_block(struct super_block *sb, struct apfs_omap *omap,
u64 id, u64 *block, bool write);
extern int apfs_create_omap_rec(struct super_block *sb, u64 oid, u64 bno);
@@ -828,7 +842,7 @@ extern int apfs_rename(struct inode *old_dir, struct dentry *old_dentry,
unsigned int flags);
extern int apfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
bool excl);
#else
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
extern int apfs_mknod(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode, dev_t rdev);
extern int apfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
@@ -838,6 +852,16 @@ extern int apfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
struct dentry *new_dentry, unsigned int flags);
extern int apfs_create(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode, bool excl);
#else
extern int apfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode, dev_t rdev);
extern int apfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode);
extern int apfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
struct dentry *old_dentry, struct inode *new_dir,
struct dentry *new_dentry, unsigned int flags);
extern int apfs_create(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode, bool excl);
#endif
extern int apfs_link(struct dentry *old_dentry, struct inode *dir,
@@ -861,6 +885,8 @@ 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);
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);
extern int apfs_clone_extents(struct apfs_dstream_info *dstream, u64 new_id);
/* file.c */
extern int apfs_fsync(struct file *file, loff_t start, loff_t end, int datasync);
@@ -874,13 +900,20 @@ extern struct inode *apfs_new_inode(struct inode *dir, umode_t mode,
dev_t rdev);
extern int apfs_create_inode_rec(struct super_block *sb, struct inode *inode,
struct dentry *dentry);
extern int apfs_inode_create_exclusive_dstream(struct inode *inode);
extern int APFS_CREATE_INODE_REC_MAXOPS(void);
extern int __apfs_write_begin(struct file *file, struct address_space *mapping, loff_t pos, unsigned int len, unsigned int flags, struct page **pagep, void **fsdata);
extern int __apfs_write_end(struct file *file, struct address_space *mapping, loff_t pos, unsigned int len, unsigned int copied, struct page *page, void *fsdata);
extern int apfs_dstream_adj_refcnt(struct apfs_dstream_info *dstream, u32 delta);
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
extern int apfs_setattr(struct dentry *dentry, struct iattr *iattr);
#else
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
extern int apfs_setattr(struct user_namespace *mnt_userns,
struct dentry *dentry, struct iattr *iattr);
#else
extern int apfs_setattr(struct mnt_idmap *idmap,
struct dentry *dentry, struct iattr *iattr);
#endif
extern int apfs_update_time(struct inode *inode, struct timespec64 *time, int flags);
@@ -893,16 +926,23 @@ extern int apfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
extern int apfs_getattr(const struct path *path, struct kstat *stat,
u32 request_mask, unsigned int query_flags);
#else
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
extern int apfs_getattr(struct user_namespace *mnt_userns,
const struct path *path, struct kstat *stat, u32 request_mask,
unsigned int query_flags);
#else
extern int apfs_getattr(struct mnt_idmap *idmap,
const struct path *path, struct kstat *stat, u32 request_mask,
unsigned int query_flags);
#endif
extern int apfs_crypto_adj_refcnt(struct super_block *sb, u64 crypto_id, int delta);
extern int APFS_CRYPTO_ADJ_REFCNT_MAXOPS(void);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
extern int apfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
extern int apfs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, struct fileattr *fa);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
extern int apfs_fileattr_get(struct dentry *dentry, struct fileattr *fa);
extern int apfs_fileattr_set(struct user_namespace *mnt_userns, struct dentry *dentry, struct fileattr *fa);
#endif
@@ -919,8 +959,7 @@ extern int apfs_read_snap_meta_key(void *raw, int size, struct apfs_key *key);
extern int apfs_read_omap_snap_key(void *raw, int size, struct apfs_key *key);
/* message.c */
extern __printf(3, 4)
void apfs_msg(struct super_block *sb, const char *prefix, const char *fmt, ...);
extern __printf(5, 6) void apfs_msg(struct super_block *sb, const char *prefix, const char *func, int line, const char *fmt, ...);
/* node.c */
extern struct apfs_node *apfs_read_node(struct super_block *sb, u64 oid,
@@ -957,6 +996,7 @@ extern int apfs_switch_to_snapshot(struct super_block *sb);
/* spaceman.c */
extern int apfs_read_spaceman(struct super_block *sb);
extern int apfs_free_queue_insert_nocache(struct super_block *sb, u64 bno, u64 count);
extern int apfs_free_queue_insert(struct super_block *sb, u64 bno, u64 count);
extern int apfs_spaceman_allocate_block(struct super_block *sb, u64 *bno, bool backwards);
+5 -5
View File
@@ -49,11 +49,11 @@
#define APFS_OBJECT_TYPE_GBITMAP 0x00000019
#define APFS_OBJECT_TYPE_GBITMAP_TREE 0x0000001a
#define APFS_OBJECT_TYPE_GBITMAP_BLOCK 0x0000001b
#define OBJECT_TYPE_ER_RECOVERY_BLOCK 0x0000001c
#define OBJECT_TYPE_SNAP_META_EXT 0x0000001d
#define OBJECT_TYPE_INTEGRITY_META 0x0000001e
#define OBJECT_TYPE_FEXT_TREE 0x0000001f
#define OBJECT_TYPE_RESERVED_20 0x00000020
#define APFS_OBJECT_TYPE_ER_RECOVERY_BLOCK 0x0000001c
#define APFS_OBJECT_TYPE_SNAP_META_EXT 0x0000001d
#define APFS_OBJECT_TYPE_INTEGRITY_META 0x0000001e
#define APFS_OBJECT_TYPE_FEXT_TREE 0x0000001f
#define APFS_OBJECT_TYPE_RESERVED_20 0x00000020
#define APFS_OBJECT_TYPE_INVALID 0x00000000
#define APFS_OBJECT_TYPE_TEST 0x000000ff
+88 -51
View File
@@ -35,13 +35,17 @@ static int apfs_child_from_query(struct apfs_query *query, u64 *child)
if (query->flags & APFS_QUERY_CAT && apfs_is_sealed(sb)) {
struct apfs_btn_index_node_val *index_val = NULL;
if (query->len != sizeof(*index_val))
if (query->len != sizeof(*index_val)) {
apfs_err(sb, "bad sealed index value length (%d)", query->len);
return -EFSCORRUPTED;
}
index_val = (struct apfs_btn_index_node_val *)(raw + query->off);
*child = le64_to_cpu(index_val->binv_child_oid) + apfs_catalog_base_oid(query);
} else {
if (query->len != 8) /* The data on a nonleaf node is the child id */
if (query->len != 8) { /* The data on a nonleaf node is the child id */
apfs_err(sb, "bad index value length (%d)", query->len);
return -EFSCORRUPTED;
}
*child = le64_to_cpup((__le64 *)(raw + query->off));
}
return 0;
@@ -190,8 +194,11 @@ int apfs_omap_lookup_block(struct super_block *sb, struct apfs_omap *omap,
query->flags |= APFS_QUERY_OMAP;
ret = apfs_btree_query(sb, &query);
if (ret)
if (ret) {
if (ret != -ENODATA)
apfs_err(sb, "query failed for oid 0x%llx, xid 0x%llx", id, apfs_mounted_xid(sb));
goto fail;
}
ret = apfs_omap_map_from_query(query, &map);
if (ret) {
@@ -211,6 +218,7 @@ int apfs_omap_lookup_block(struct super_block *sb, struct apfs_omap *omap,
new_bh = apfs_read_object_block(sb, *block, write, preserve);
if (IS_ERR(new_bh)) {
apfs_err(sb, "CoW failed for oid 0x%llx, xid 0x%llx", id, apfs_mounted_xid(sb));
ret = PTR_ERR(new_bh);
goto fail;
}
@@ -225,6 +233,8 @@ int apfs_omap_lookup_block(struct super_block *sb, struct apfs_omap *omap,
ret = apfs_btree_insert(query, &key, sizeof(key), &val, sizeof(val));
else
ret = apfs_btree_replace(query, &key, sizeof(key), &val, sizeof(val));
if (ret)
apfs_err(sb, "CoW omap update failed (oid 0x%llx, xid 0x%llx)", id, apfs_mounted_xid(sb));
*block = new_bh->b_blocknr;
brelse(new_bh);
@@ -265,8 +275,10 @@ int apfs_create_omap_rec(struct super_block *sb, u64 oid, u64 bno)
query->flags |= APFS_QUERY_OMAP;
ret = apfs_btree_query(sb, &query);
if (ret && ret != -ENODATA)
if (ret && ret != -ENODATA) {
apfs_err(sb, "query failed for oid 0x%llx, bno 0x%llx", oid, bno);
goto fail;
}
raw_key.ok_oid = cpu_to_le64(oid);
raw_key.ok_xid = cpu_to_le64(nxi->nx_xid);
@@ -276,8 +288,10 @@ int apfs_create_omap_rec(struct super_block *sb, u64 oid, u64 bno)
ret = apfs_btree_insert(query, &raw_key, sizeof(raw_key),
&raw_val, sizeof(raw_val));
if (ret)
if (ret) {
apfs_err(sb, "insertion failed for oid 0x%llx, bno 0x%llx", oid, bno);
goto fail;
}
apfs_omap_cache_save(omap, oid, bno);
@@ -311,13 +325,23 @@ int apfs_delete_omap_rec(struct super_block *sb, u64 oid)
query->flags |= APFS_QUERY_OMAP;
ret = apfs_btree_query(sb, &query);
if (ret == -ENODATA)
if (ret == -ENODATA) {
apfs_err(sb, "nonexistent record (oid 0x%llx)", oid);
ret = -EFSCORRUPTED;
if (!ret)
ret = apfs_btree_remove(query);
if (!ret)
apfs_omap_cache_delete(omap, oid);
goto fail;
}
if (ret) {
apfs_err(sb, "query failed (oid 0x%llx)", oid);
goto fail;
}
ret = apfs_btree_remove(query);
if (ret) {
apfs_err(sb, "removal failed (oid 0x%llx)", oid);
goto fail;
}
apfs_omap_cache_delete(omap, oid);
fail:
apfs_free_query(query);
return ret;
}
@@ -409,8 +433,10 @@ static int apfs_query_set_before_first(struct super_block *sb, struct apfs_query
/* Now go a level deeper */
node = apfs_read_node(sb, child_id, storage, false /* write */);
if (IS_ERR(node))
if (IS_ERR(node)) {
apfs_err(sb, "failed to read child 0x%llx of node 0x%llx", child_id, (*query)->node->object.oid);
return PTR_ERR(node);
}
parent = *query;
*query = apfs_alloc_query(node, parent);
@@ -422,7 +448,7 @@ static int apfs_query_set_before_first(struct super_block *sb, struct apfs_query
node = NULL;
}
apfs_alert(sb, "b-tree is corrupted");
apfs_err(sb, "btree is too high");
return -EFSCORRUPTED;
}
@@ -455,7 +481,7 @@ next_node:
* forever if the filesystem is damaged. 12 should be more
* than enough to map every block.
*/
apfs_alert(sb, "b-tree is corrupted");
apfs_err(sb, "btree is too high");
return -EFSCORRUPTED;
}
@@ -466,8 +492,10 @@ next_node:
* let the query give up at the root node.
*/
err = apfs_query_set_before_first(sb, query);
if (err)
if (err) {
apfs_err(sb, "failed to set before the first record");
return err;
}
return -ENODATA;
} else if (err == -EAGAIN) {
if (!(*query)->parent) /* We are at the root of the tree */
@@ -494,8 +522,10 @@ next_node:
/* Now go a level deeper and search the child */
node = apfs_read_node(sb, child_id, storage, false /* write */);
if (IS_ERR(node))
if (IS_ERR(node)) {
apfs_err(sb, "failed to read node 0x%llx", child_id);
return PTR_ERR(node);
}
if (node->object.oid != child_id)
apfs_debug(sb, "corrupt b-tree");
@@ -515,28 +545,6 @@ next_node:
goto next_node;
}
/**
* apfs_omap_read_node - Find and read a node from a b-tree
* @sb: filesystem superblock
* @id: id for the seeked node
*
* Returns NULL is case of failure, otherwise a pointer to the resulting
* apfs_node structure.
*/
struct apfs_node *apfs_omap_read_node(struct super_block *sb, u64 id)
{
struct apfs_node *result;
result = apfs_read_node(sb, id, APFS_OBJ_VIRTUAL, false /* write */);
if (IS_ERR(result))
return result;
if (result->object.oid != id)
apfs_debug(sb, "corrupt b-tree");
return result;
}
/**
* apfs_query_join_transaction - Add the found node to the current transaction
* @query: query that found the node
@@ -556,8 +564,10 @@ int apfs_query_join_transaction(struct apfs_query *query)
ASSERT(!apfs_node_is_root(node));
node = apfs_read_node(sb, oid, storage, true /* write */);
if (IS_ERR(node))
if (IS_ERR(node)) {
apfs_err(sb, "Cow failed for node 0x%llx", oid);
return PTR_ERR(node);
}
apfs_node_free(query->node);
query->node = node;
@@ -665,18 +675,20 @@ static int apfs_query_refresh(struct apfs_query *old_query)
* which are only expected when large inline xattr values are involved.
*/
if ((old_query->flags & APFS_QUERY_TREE_MASK) != APFS_QUERY_CAT) {
apfs_warn(sb, "attempt to refresh a non-catalog query");
apfs_err(sb, "non-catalog query");
return -EFSCORRUPTED;
}
if (!apfs_node_is_leaf(node)) {
apfs_warn(sb, "attempt to refresh a non-leaf query");
apfs_err(sb, "non-leaf query");
return -EFSCORRUPTED;
}
/* Build a new query that points exactly to the same key */
err = apfs_read_cat_key(raw + old_query->key_off, old_query->key_len, &new_key, hashed);
if (err)
if (err) {
apfs_err(sb, "failed to read the key");
return err;
}
new_query = apfs_alloc_query(APFS_SB(sb)->s_cat_root, NULL /* parent */);
if (!new_query)
return -ENOMEM;
@@ -684,8 +696,10 @@ static int apfs_query_refresh(struct apfs_query *old_query)
new_query->flags = APFS_QUERY_CAT | APFS_QUERY_EXACT;
err = apfs_btree_query(sb, &new_query);
if (err)
if (err) {
apfs_err(sb, "failed to rerun");
goto fail;
}
/* Set the original query flags and key on the new query */
for (ancestor = new_query; ancestor; ancestor = ancestor->parent) {
@@ -739,6 +753,7 @@ int apfs_btree_insert(struct apfs_query *query, void *key, int key_len,
void *val, int val_len)
{
struct apfs_node *node = query->node;
struct super_block *sb = node->object.sb;
struct apfs_btree_node_phys *node_raw;
int err;
@@ -748,8 +763,10 @@ int apfs_btree_insert(struct apfs_query *query, void *key, int key_len,
key_len, val_len);
err = apfs_query_join_transaction(query);
if (err)
if (err) {
apfs_err(sb, "query join failed");
return err;
}
again:
node = query->node;
@@ -760,18 +777,23 @@ again:
if (err == -ENOSPC) {
if (!query->parent && !apfs_node_is_root(node)) {
err = apfs_query_refresh(query);
if (err)
if (err) {
apfs_err(sb, "query refresh failed");
return err;
}
if (node->records == 1) {
/* The new record just won't fit in the node */
return apfs_create_single_rec_node(query, key, key_len, val, val_len);
}
}
err = apfs_node_split(query);
if (err)
if (err) {
apfs_err(sb, "node split failed");
return err;
}
goto again;
} else if (err) {
apfs_err(sb, "node record insertion failed");
return err;
}
@@ -779,6 +801,8 @@ again:
if (query->parent && query->index == 0) {
err = apfs_btree_replace(query->parent, key, key_len,
NULL /* val */, 0 /* val_len */);
if (err)
apfs_err(sb, "parent update failed");
}
return err;
}
@@ -792,6 +816,7 @@ again:
int apfs_btree_remove(struct apfs_query *query)
{
struct apfs_node *node = query->node;
struct super_block *sb = node->object.sb;
struct apfs_btree_node_phys *node_raw;
int later_entries = node->records - query->index - 1;
int err;
@@ -804,8 +829,10 @@ int apfs_btree_remove(struct apfs_query *query)
apfs_btree_change_node_count(query, -1 /* change */);
err = apfs_query_join_transaction(query);
if (err)
if (err) {
apfs_err(sb, "query join failed");
return err;
}
node = query->node;
node_raw = (void *)query->node->object.data;
@@ -834,8 +861,10 @@ int apfs_btree_remove(struct apfs_query *query)
err = apfs_btree_replace(query->parent, key, first_key_len,
NULL /* val */, 0 /* val_len */);
if (err)
if (err) {
apfs_err(sb, "parent update failed");
return err;
}
}
/* Remove the entry from the table of contents */
@@ -902,8 +931,10 @@ int apfs_btree_replace(struct apfs_query *query, void *key, int key_len,
}
err = apfs_query_join_transaction(query);
if (err)
if (err) {
apfs_err(sb, "query join failed");
return err;
}
again:
node = query->node;
@@ -914,8 +945,10 @@ again:
if (key && query->parent && query->index == 0) {
err = apfs_btree_replace(query->parent, key, key_len,
NULL /* val */, 0 /* val_len */);
if (err)
if (err) {
apfs_err(sb, "parent update failed");
return err;
}
}
err = apfs_node_replace(query, key, key_len, val, val_len);
@@ -927,12 +960,16 @@ again:
return -EFSCORRUPTED;
}
err = apfs_query_refresh(query);
if (err)
if (err) {
apfs_err(sb, "query refresh failed");
return err;
}
}
err = apfs_node_split(query);
if (err)
if (err) {
apfs_err(sb, "node split failed");
return err;
}
goto again;
}
return err;
+93 -29
View File
@@ -47,6 +47,7 @@ struct apfs_compress_file_data {
ssize_t bufblk;
size_t bufsize;
struct mutex mtx;
struct super_block *sb;
};
static inline int apfs_compress_is_rsrc(u32 algo)
@@ -77,6 +78,7 @@ static inline bool apfs_compress_is_supported(u32 algo)
static int apfs_compress_file_open(struct inode *inode, struct file *filp)
{
struct super_block *sb = inode->i_sb;
struct apfs_compress_file_data *fd;
ssize_t res;
u8 *tmp = NULL, *cdata;
@@ -89,10 +91,13 @@ static int apfs_compress_file_open(struct inode *inode, struct file *filp)
if(!fd)
return -ENOMEM;
mutex_init(&fd->mtx);
fd->sb = sb;
res = ____apfs_xattr_get(inode, APFS_XATTR_NAME_COMPRESSED, &fd->hdr, sizeof(fd->hdr), 0);
if(res != sizeof(fd->hdr))
if(res != sizeof(fd->hdr)) {
apfs_err(sb, "decmpfs header read failed");
goto fail;
}
if(!apfs_compress_is_supported(le32_to_cpu(fd->hdr.algo))) {
res = -EOPNOTSUPP;
@@ -106,11 +111,15 @@ static int apfs_compress_file_open(struct inode *inode, struct file *filp)
fd->bufblk = -1;
res = ____apfs_xattr_get(inode, APFS_XATTR_NAME_RSRC_FORK, NULL, 0, 0);
if(res < 0)
if(res < 0) {
apfs_err(sb, "fork size read failed");
goto fail;
}
if(res > MAX_FBUF_SIZE)
if(res > MAX_FBUF_SIZE) {
apfs_err(sb, "fork too big");
goto fail_enomem;
}
fd->size = res;
fd->data = kvmalloc(res, GFP_KERNEL);
@@ -118,11 +127,15 @@ static int apfs_compress_file_open(struct inode *inode, struct file *filp)
goto fail_enomem;
res = ____apfs_xattr_get(inode, APFS_XATTR_NAME_RSRC_FORK, fd->data, fd->size, 1);
if(res != fd->size)
if(res != fd->size) {
apfs_err(sb, "fork read failed");
goto fail;
}
} else {
if(le64_to_cpu(fd->hdr.size) > MAX_FBUF_SIZE)
if(le64_to_cpu(fd->hdr.size) > MAX_FBUF_SIZE) {
apfs_err(sb, "decmpfs too big");
goto fail_enomem;
}
fd->size = le64_to_cpu(fd->hdr.size);
fd->data = kvmalloc(le64_to_cpu(fd->hdr.size), GFP_KERNEL);
@@ -130,11 +143,15 @@ static int apfs_compress_file_open(struct inode *inode, struct file *filp)
goto fail_enomem;
res = ____apfs_xattr_get(inode, APFS_XATTR_NAME_COMPRESSED, NULL, 0, 0);
if(res < sizeof(fd->hdr) + 1)
if(res < sizeof(fd->hdr) + 1) {
apfs_err(sb, "decmpfs size read failed");
goto fail;
}
csize = res - sizeof(fd->hdr);
if(res > MAX_FBUF_SIZE)
if(res > MAX_FBUF_SIZE) {
apfs_err(sb, "decmpfs too big");
goto fail_enomem;
}
tmp = kvmalloc(res, GFP_KERNEL);
if(!tmp)
@@ -142,26 +159,36 @@ static int apfs_compress_file_open(struct inode *inode, struct file *filp)
cdata = tmp + sizeof(fd->hdr);
res = ____apfs_xattr_get(inode, APFS_XATTR_NAME_COMPRESSED, tmp, csize + sizeof(fd->hdr), 1);
if(res != csize + sizeof(fd->hdr))
if(res != csize + sizeof(fd->hdr)) {
apfs_err(sb, "decmpfs read failed");
goto fail;
}
switch(le32_to_cpu(fd->hdr.algo)) {
case APFS_COMPRESS_ZLIB_ATTR:
if(cdata[0] == 0x78 && csize >= 2) {
res = zlib_inflate_blob(fd->data, fd->size, cdata + 2, csize - 2);
if(res != fd->size)
if(res != fd->size) {
apfs_err(sb, "zlib attr read failed");
goto fail;
}
} else if((cdata[0] & 0x0F) == 0x0F) {
if(csize - 1 != fd->size)
if(csize - 1 != fd->size) {
apfs_err(sb, "zlib attr read failed");
goto fail_einval;
}
memcpy(fd->data, cdata + 1, csize - 1);
} else
} else {
apfs_err(sb, "zlib attr read failed");
goto fail_einval;
}
break;
case APFS_COMPRESS_LZVN_ATTR:
if(cdata[0] == 0x06) {
if(csize - 1 != fd->size)
if(csize - 1 != fd->size) {
apfs_err(sb, "lzvn attr read failed");
goto fail_einval;
}
memcpy(fd->data, cdata + 1, csize - 1);
} else {
lzvn_decoder_state dstate = {0};
@@ -171,45 +198,60 @@ static int apfs_compress_file_open(struct inode *inode, struct file *filp)
dstate.dst = dstate.dst_begin = fd->data;
dstate.dst_end = dstate.dst + fd->size;
lzvn_decode(&dstate);
if(dstate.dst != fd->data + fd->size)
if(dstate.dst != fd->data + fd->size) {
apfs_err(sb, "lzvn attr read failed");
goto fail_einval;
}
}
break;
case APFS_COMPRESS_LZBITMAP_ATTR:
if(cdata[0] == 0x5a) {
size_t out_len;
res = zbm_decompress(fd->data, fd->size, cdata, csize, &out_len);
if(res < 0)
if(res < 0) {
apfs_err(sb, "lzbitmap attr read failed");
goto fail;
if(out_len != fd->size)
}
if(out_len != fd->size) {
apfs_err(sb, "lzbitmap attr read failed");
goto fail_einval;
}
} else if((cdata[0] & 0x0F) == 0x0F) {
if(csize - 1 != fd->size)
if(csize - 1 != fd->size) {
apfs_err(sb, "lzbitmap attr read failed");
goto fail_einval;
}
memcpy(fd->data, cdata + 1, csize - 1);
} else {
apfs_err(sb, "lzbitmap attr read failed");
goto fail_einval;
}
break;
case APFS_COMPRESS_LZFSE_ATTR:
if(cdata[0] == 0x62 && csize >= 2) {
res = lzfse_decode_buffer(fd->data, fd->size, cdata, csize, NULL);
if(res != fd->size)
if(res != fd->size) {
apfs_err(sb, "lzfse attr read failed");
/* Could be ENOMEM too... */
goto fail_einval;
}
} else {
/*
* I've never encountered this, but I assume
* it's like the resource version.
*/
if(csize - 1 != fd->size)
if(csize - 1 != fd->size) {
apfs_err(sb, "lzfse attr read failed");
goto fail_einval;
}
memcpy(fd->data, cdata + 1, csize - 1);
}
break;
case APFS_COMPRESS_PLAIN_ATTR:
if(csize - 1 != fd->size)
if(csize - 1 != fd->size) {
apfs_err(sb, "plain attr read failed");
goto fail_einval;
}
memcpy(fd->data, cdata + 1, csize - 1);
break;
default:
@@ -242,6 +284,7 @@ fail:
static ssize_t apfs_compress_file_read_block(struct apfs_compress_file_data *fd, char __user *buf, size_t size, loff_t off)
{
struct super_block *sb = fd->sb;
u32 doffs = 0, coffs;
loff_t block;
u8 *cdata, *tmp = fd->buf;
@@ -262,14 +305,20 @@ static ssize_t apfs_compress_file_read_block(struct apfs_compress_file_data *fd,
struct apfs_compress_rsrc_hdr *hdr = fd->data;
struct apfs_compress_rsrc_data *cd = NULL;
if(fd->size < sizeof(*hdr))
if(fd->size < sizeof(*hdr)) {
apfs_err(sb, "resource header doesn't fit");
return -EINVAL;
}
doffs = be32_to_cpu(hdr->data_offs);
if(doffs >= fd->size || fd->size - doffs < sizeof(*cd))
if(doffs >= fd->size || fd->size - doffs < sizeof(*cd)) {
apfs_err(sb, "invalid data header offset in resource");
return -EINVAL;
}
cd = fd->data + doffs;
if(fd->size - doffs - sizeof(*cd) < sizeof(cd->block[0]) * (size_t)le32_to_cpu(cd->num))
if(fd->size - doffs - sizeof(*cd) < sizeof(cd->block[0]) * (size_t)le32_to_cpu(cd->num)) {
apfs_err(sb, "resource data header won't fit");
return -EINVAL;
}
if(block >= le32_to_cpu(cd->num))
return 0;
@@ -283,8 +332,10 @@ static ssize_t apfs_compress_file_read_block(struct apfs_compress_file_data *fd,
} else {
__le32 *block_offs = fd->data + doffs;
if((size_t)fd->size < sizeof(*block_offs) * ((size_t)block + 2))
if((size_t)fd->size < sizeof(*block_offs) * ((size_t)block + 2)) {
apfs_err(sb, "resource data header won't fit");
return -EINVAL;
}
bsize = le64_to_cpu(fd->hdr.size) - block * APFS_COMPRESS_BLOCK;
if(bsize > APFS_COMPRESS_BLOCK)
@@ -293,22 +344,28 @@ static ssize_t apfs_compress_file_read_block(struct apfs_compress_file_data *fd,
coffs = le32_to_cpu(block_offs[block]);
csize = le32_to_cpu(block_offs[block + 1]) - coffs;
}
if(coffs >= fd->size - doffs || fd->size - doffs - coffs < csize || csize > APFS_COMPRESS_BLOCK + 1)
if(coffs >= fd->size - doffs || fd->size - doffs - coffs < csize || csize > APFS_COMPRESS_BLOCK + 1) {
apfs_err(sb, "invalid block size/offset in resource");
return -EINVAL;
}
cdata = fd->data + doffs + coffs;
switch(le32_to_cpu(fd->hdr.algo)) {
case APFS_COMPRESS_ZLIB_RSRC:
if(cdata[0] == 0x78 && csize >= 2) {
res = zlib_inflate_blob(tmp, bsize, cdata + 2, csize - 2);
if(res <= 0)
if(res <= 0) {
apfs_err(sb, "zlib rsrc read failed");
return res;
}
bsize = res;
} else if((cdata[0] & 0x0F) == 0x0F) {
memcpy(tmp, &cdata[1], csize - 1);
bsize = csize - 1;
} else
} else {
apfs_err(sb, "zlib rsrc read failed");
return -EINVAL;
}
break;
case APFS_COMPRESS_LZVN_RSRC:
if(cdata[0] == 0x06) {
@@ -328,21 +385,26 @@ static ssize_t apfs_compress_file_read_block(struct apfs_compress_file_data *fd,
case APFS_COMPRESS_LZBITMAP_RSRC:
if(cdata[0] == 0x5a) {
res = zbm_decompress(tmp, bsize, cdata, csize, &bsize);
if(res < 0)
if(res < 0) {
apfs_err(sb, "lzbitmap rsrc read failed");
return res;
}
} else if((cdata[0] & 0x0F) == 0x0F) {
memcpy(tmp, &cdata[1], csize - 1);
bsize = csize - 1;
} else {
apfs_err(sb, "lzbitmap rsrc read failed");
return -EINVAL;
}
break;
case APFS_COMPRESS_LZFSE_RSRC:
if(cdata[0] == 0x62 && csize >= 2) {
res = lzfse_decode_buffer(tmp, bsize, cdata, csize, NULL);
if(res == 0)
if(res == 0) {
apfs_err(sb, "lzfse rsrc read failed");
/* Could be ENOMEM too... */
return -EINVAL;
}
bsize = res;
} else {
/* cdata[0] == 0xff, apparently */
@@ -436,8 +498,10 @@ int apfs_compress_get_size(struct inode *inode, loff_t *size)
if(res < 0)
return res;
if(res != sizeof(hdr))
if(res != sizeof(hdr)) {
apfs_err(inode->i_sb, "decmpfs header read failed");
return 1;
}
if(!apfs_compress_is_supported(le32_to_cpu(hdr.algo)))
return 1;
+159 -48
View File
@@ -22,6 +22,7 @@
*/
static int apfs_drec_from_query(struct apfs_query *query, struct apfs_drec *drec, bool hashed)
{
struct super_block *sb = query->node->object.sb;
char *raw = query->node->object.data;
struct apfs_drec_hashed_key *de_hkey = NULL;
struct apfs_drec_key *de_ukey = NULL;
@@ -30,28 +31,37 @@ static int apfs_drec_from_query(struct apfs_query *query, struct apfs_drec *drec
char *xval = NULL, *name;
namelen = query->key_len - (hashed ? sizeof(*de_hkey) : sizeof(*de_ukey));
if (namelen < 1)
if (namelen < 1) {
apfs_err(sb, "key is too small (%d)", query->key_len);
return -EFSCORRUPTED;
if (query->len < sizeof(*de))
}
if (query->len < sizeof(*de)) {
apfs_err(sb, "value is too small (%d)", query->len);
return -EFSCORRUPTED;
}
de = (struct apfs_drec_val *)(raw + query->off);
if (hashed) {
de_hkey = (struct apfs_drec_hashed_key *)(raw + query->key_off);
if (namelen != (le32_to_cpu(de_hkey->name_len_and_hash) &
APFS_DREC_LEN_MASK))
if (namelen != (le32_to_cpu(de_hkey->name_len_and_hash) & APFS_DREC_LEN_MASK)) {
apfs_err(sb, "inconsistent name length");
return -EFSCORRUPTED;
}
name = de_hkey->name;
} else {
de_ukey = (struct apfs_drec_key *)(raw + query->key_off);
if (namelen != le16_to_cpu(de_ukey->name_len))
if (namelen != le16_to_cpu(de_ukey->name_len)) {
apfs_err(sb, "inconsistent name length");
return -EFSCORRUPTED;
}
name = de_ukey->name;
}
/* Filename must be NULL-terminated */
if (name[namelen - 1] != 0)
if (name[namelen - 1] != 0) {
apfs_err(sb, "null termination missing");
return -EFSCORRUPTED;
}
/* The dentry may have at most one xfield: the sibling id */
drec->sibling_id = 0;
@@ -121,6 +131,8 @@ static struct apfs_query *apfs_dentry_lookup(struct inode *dir,
return query;
fail:
if (err != -ENODATA)
apfs_err(sb, "query failed in dir 0x%llx", cnid);
apfs_free_query(query);
return ERR_PTR(err);
}
@@ -365,8 +377,10 @@ static int apfs_create_dentry_rec(struct inode *inode, struct qstr *qname,
query->flags |= APFS_QUERY_CAT;
ret = apfs_btree_query(sb, &query);
if (ret && ret != -ENODATA)
if (ret && ret != -ENODATA) {
apfs_err(sb, "query failed in dir 0x%llx (hash 0x%llx)", parent_id, key.number);
goto fail;
}
if (hashed)
key_len = apfs_build_dentry_hashed_key(qname, key.number, parent_id,
@@ -386,6 +400,8 @@ static int apfs_create_dentry_rec(struct inode *inode, struct qstr *qname,
}
/* TODO: deal with hash collisions */
ret = apfs_btree_insert(query, raw_key, key_len, raw_val, val_len);
if (ret)
apfs_err(sb, "insertion failed in dir 0x%llx (hash 0x%llx)", parent_id, key.number);
fail:
kfree(raw_val);
@@ -452,8 +468,10 @@ static int apfs_create_sibling_link_rec(struct dentry *dentry,
query->flags |= APFS_QUERY_CAT;
ret = apfs_btree_query(sb, &query);
if (ret && ret != -ENODATA)
if (ret && ret != -ENODATA) {
apfs_err(sb, "query failed for ino 0x%llx, sibling 0x%llx", apfs_ino(inode), sibling_id);
goto fail;
}
apfs_key_set_hdr(APFS_TYPE_SIBLING_LINK, apfs_ino(inode), &raw_key);
raw_key.sibling_id = cpu_to_le64(sibling_id);
@@ -461,8 +479,9 @@ static int apfs_create_sibling_link_rec(struct dentry *dentry,
if (val_len < 0)
goto fail;
ret = apfs_btree_insert(query, &raw_key, sizeof(raw_key),
raw_val, val_len);
ret = apfs_btree_insert(query, &raw_key, sizeof(raw_key), raw_val, val_len);
if (ret)
apfs_err(sb, "insertion failed for ino 0x%llx, sibling 0x%llx", apfs_ino(inode), sibling_id);
kfree(raw_val);
fail:
@@ -498,14 +517,17 @@ static int apfs_create_sibling_map_rec(struct dentry *dentry,
query->flags |= APFS_QUERY_CAT;
ret = apfs_btree_query(sb, &query);
if (ret && ret != -ENODATA)
if (ret && ret != -ENODATA) {
apfs_err(sb, "query failed for sibling 0x%llx", sibling_id);
goto fail;
}
apfs_key_set_hdr(APFS_TYPE_SIBLING_MAP, sibling_id, &raw_key);
raw_val.file_id = cpu_to_le64(apfs_ino(inode));
ret = apfs_btree_insert(query, &raw_key, sizeof(raw_key),
&raw_val, sizeof(raw_val));
ret = apfs_btree_insert(query, &raw_key, sizeof(raw_key), &raw_val, sizeof(raw_val));
if (ret)
apfs_err(sb, "insertion failed for sibling 0x%llx", sibling_id);
fail:
apfs_free_query(query);
@@ -558,6 +580,7 @@ static int apfs_create_sibling_recs(struct dentry *dentry,
*/
static int apfs_create_dentry(struct dentry *dentry, struct inode *inode)
{
struct super_block *sb = inode->i_sb;
struct inode *parent = d_inode(dentry->d_parent);
u64 sibling_id = 0;
int err;
@@ -565,14 +588,17 @@ static int apfs_create_dentry(struct dentry *dentry, struct inode *inode)
if (inode->i_nlink > 1) {
/* This is optional for a single link, so don't waste space */
err = apfs_create_sibling_recs(dentry, inode, &sibling_id);
if (err)
if (err) {
apfs_err(sb, "failed to create sibling recs for ino 0x%llx", apfs_ino(inode));
return err;
}
}
err = apfs_create_dentry_rec(inode, &dentry->d_name,
apfs_ino(parent), sibling_id);
if (err)
err = apfs_create_dentry_rec(inode, &dentry->d_name, apfs_ino(parent), sibling_id);
if (err) {
apfs_err(sb, "failed to create drec for ino 0x%llx", apfs_ino(inode));
return err;
}
/* Now update the parent inode */
parent->i_mtime = parent->i_ctime = current_time(inode);
@@ -629,20 +655,28 @@ int apfs_mkany(struct inode *dir, struct dentry *dentry, umode_t mode,
}
err = apfs_create_inode_rec(sb, inode, dentry);
if (err)
if (err) {
apfs_err(sb, "failed to create inode rec for ino 0x%llx", apfs_ino(inode));
goto out_discard_inode;
}
err = apfs_create_dentry(dentry, inode);
if (err)
if (err) {
apfs_err(sb, "failed to create dentry recs for ino 0x%llx", apfs_ino(inode));
goto out_discard_inode;
}
if (symname) {
err = apfs_xattr_set(inode, APFS_XATTR_NAME_SYMLINK, symname,
strlen(symname) + 1, 0 /* flags */);
if (err == -ERANGE)
if (err == -ERANGE) {
err = -ENAMETOOLONG;
if (err)
goto out_undo_create;
}
if (err) {
apfs_err(sb, "failed to set symlink xattr for ino 0x%llx", apfs_ino(inode));
goto out_undo_create;
}
}
err = apfs_transaction_commit(sb);
@@ -671,9 +705,12 @@ out_abort:
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
int apfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode,
dev_t rdev)
#else
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
int apfs_mknod(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode, dev_t rdev)
#else
int apfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode, dev_t rdev)
#endif
{
return apfs_mkany(dir, dentry, mode, rdev, NULL /* symname */);
@@ -686,7 +723,7 @@ int apfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
return apfs_mknod(dir, dentry, mode | S_IFDIR, 0 /* rdev */);
}
#else
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
int apfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode)
@@ -694,6 +731,14 @@ int apfs_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
return apfs_mknod(mnt_userns, dir, dentry, mode | S_IFDIR, 0 /* rdev */);
}
#else
int apfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode)
{
return apfs_mknod(idmap, dir, dentry, mode | S_IFDIR, 0 /* rdev */);
}
#endif
@@ -705,7 +750,7 @@ int apfs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
return apfs_mknod(dir, dentry, mode, 0 /* rdev */);
}
#else
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
int apfs_create(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, umode_t mode, bool excl)
@@ -713,6 +758,14 @@ int apfs_create(struct user_namespace *mnt_userns, struct inode *dir,
return apfs_mknod(mnt_userns, dir, dentry, mode, 0 /* rdev */);
}
#else
int apfs_create(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, umode_t mode, bool excl)
{
return apfs_mknod(idmap, dir, dentry, mode, 0 /* rdev */);
}
#endif
@@ -725,14 +778,17 @@ int apfs_create(struct user_namespace *mnt_userns, struct inode *dir,
static int apfs_prepare_dentry_for_link(struct dentry *dentry)
{
struct inode *parent = d_inode(dentry->d_parent);
struct super_block *sb = parent->i_sb;
struct apfs_query *query;
struct apfs_drec drec;
u64 sibling_id;
int ret;
query = apfs_dentry_lookup(parent, &dentry->d_name, &drec);
if (IS_ERR(query))
if (IS_ERR(query)) {
apfs_err(sb, "lookup failed in dir 0x%llx", apfs_ino(parent));
return PTR_ERR(query);
}
if (drec.sibling_id) {
/* This dentry already has a sibling id xfield */
apfs_free_query(query);
@@ -742,12 +798,16 @@ static int apfs_prepare_dentry_for_link(struct dentry *dentry)
/* Don't modify the dentry record, just delete it to make a new one */
ret = apfs_btree_remove(query);
apfs_free_query(query);
if (ret)
if (ret) {
apfs_err(sb, "removal failed in dir 0x%llx", apfs_ino(parent));
return ret;
}
ret = apfs_create_sibling_recs(dentry, d_inode(dentry), &sibling_id);
if (ret)
if (ret) {
apfs_err(sb, "failed to create sibling recs in dir 0x%llx", apfs_ino(parent));
return ret;
}
return apfs_create_dentry_rec(d_inode(dentry), &dentry->d_name,
apfs_ino(parent), sibling_id);
}
@@ -776,6 +836,7 @@ static void __apfs_undo_link(struct dentry *dentry, struct inode *inode)
static int __apfs_link(struct dentry *old_dentry, struct dentry *dentry)
{
struct inode *inode = d_inode(old_dentry);
struct super_block *sb = inode->i_sb;
int err;
/* First update the inode's link count */
@@ -786,13 +847,17 @@ static int __apfs_link(struct dentry *old_dentry, struct dentry *dentry)
if (inode->i_nlink == 2) {
/* A single link may lack sibling records, so create them now */
err = apfs_prepare_dentry_for_link(old_dentry);
if (err)
if (err) {
apfs_err(sb, "failed to prepare original dentry");
goto fail;
}
}
err = apfs_create_dentry(dentry, inode);
if (err)
if (err) {
apfs_err(sb, "failed to create new dentry");
goto fail;
}
return 0;
fail:
@@ -868,9 +933,13 @@ static int apfs_delete_sibling_link_rec(struct dentry *dentry, u64 sibling_id)
/* A dentry with a sibling id must have sibling records */
ret = -EFSCORRUPTED;
}
if (ret)
if (ret) {
apfs_err(sb, "query failed for ino 0x%llx, sibling 0x%llx", apfs_ino(inode), sibling_id);
goto fail;
}
ret = apfs_btree_remove(query);
if (ret)
apfs_err(sb, "removal failed for ino 0x%llx, sibling 0x%llx", apfs_ino(inode), sibling_id);
fail:
apfs_free_query(query);
@@ -907,9 +976,13 @@ static int apfs_delete_sibling_map_rec(struct dentry *dentry, u64 sibling_id)
/* A dentry with a sibling id must have sibling records */
ret = -EFSCORRUPTED;
}
if (ret)
if (ret) {
apfs_err(sb, "query failed for sibling 0x%llx", sibling_id);
goto fail;
}
ret = apfs_btree_remove(query);
if (ret)
apfs_err(sb, "removal failed for sibling 0x%llx", sibling_id);
fail:
apfs_free_query(query);
@@ -938,16 +1011,22 @@ static int apfs_delete_dentry(struct dentry *dentry)
return PTR_ERR(query);
err = apfs_btree_remove(query);
apfs_free_query(query);
if (err)
if (err) {
apfs_err(sb, "drec removal failed");
return err;
}
if (drec.sibling_id) {
err = apfs_delete_sibling_link_rec(dentry, drec.sibling_id);
if (err)
if (err) {
apfs_err(sb, "sibling link removal failed");
return err;
}
err = apfs_delete_sibling_map_rec(dentry, drec.sibling_id);
if (err)
if (err) {
apfs_err(sb, "sibling map removal failed");
return err;
}
}
/* Now update the parent inode */
@@ -986,19 +1065,26 @@ static inline void apfs_undo_delete_dentry(struct dentry *dentry)
static int apfs_sibling_link_from_query(struct apfs_query *query,
char **name, u64 *parent)
{
struct super_block *sb = query->node->object.sb;
char *raw = query->node->object.data;
struct apfs_sibling_val *siblink;
int namelen = query->len - sizeof(*siblink);
if (namelen < 1)
if (namelen < 1) {
apfs_err(sb, "value is too small (%d)", query->len);
return -EFSCORRUPTED;
}
siblink = (struct apfs_sibling_val *)(raw + query->off);
if (namelen != le16_to_cpu(siblink->name_len))
if (namelen != le16_to_cpu(siblink->name_len)) {
apfs_err(sb, "inconsistent name length");
return -EFSCORRUPTED;
}
/* Filename must be NULL-terminated */
if (siblink->name[namelen - 1] != 0)
if (siblink->name[namelen - 1] != 0) {
apfs_err(sb, "null termination missing");
return -EFSCORRUPTED;
}
*name = kmalloc(namelen, GFP_KERNEL);
if (!*name)
@@ -1041,14 +1127,20 @@ static int apfs_find_primary_link(struct inode *inode, char **name, u64 *parent)
if (err == -ENODATA) /* No more link records */
break;
kfree(*name);
if (err)
if (err) {
apfs_err(sb, "query failed for ino 0x%llx", apfs_ino(inode));
goto fail;
}
err = apfs_sibling_link_from_query(query, name, parent);
if (err)
if (err) {
apfs_err(sb, "bad sibling link record for ino 0x%llx", apfs_ino(inode));
goto fail;
}
}
err = *name ? 0 : -EFSCORRUPTED; /* Sibling records must exist */
if (err)
apfs_err(sb, "query failed for ino 0x%llx", apfs_ino(inode));
fail:
apfs_free_query(query);
@@ -1095,10 +1187,11 @@ static int apfs_create_orphan_link(struct inode *inode)
err = apfs_orphan_name(inode, &qname);
if (err)
return err;
err = apfs_create_dentry_rec(inode, &qname, apfs_ino(priv_dir),
0 /* sibling_id */);
if (err)
err = apfs_create_dentry_rec(inode, &qname, apfs_ino(priv_dir), 0 /* sibling_id */);
if (err) {
apfs_err(sb, "failed to create drec for ino 0x%llx", apfs_ino(inode));
goto fail;
}
/* Now update the child count for private-dir */
priv_dir->i_mtime = priv_dir->i_ctime = current_time(priv_dir);
@@ -1134,13 +1227,16 @@ int apfs_delete_orphan_link(struct inode *inode)
query = apfs_dentry_lookup(priv_dir, &qname, &drec);
if (IS_ERR(query)) {
apfs_err(sb, "dentry lookup failed");
err = PTR_ERR(query);
query = NULL;
goto fail;
}
err = apfs_btree_remove(query);
if (err)
if (err) {
apfs_err(sb, "dentry removal failed");
goto fail;
}
/* Now update the child count for private-dir */
priv_dir->i_mtime = priv_dir->i_ctime = current_time(priv_dir);
@@ -1209,13 +1305,16 @@ static void apfs_vol_filecnt_dec(struct inode *orphan)
static int __apfs_unlink(struct inode *dir, struct dentry *dentry)
{
struct inode *inode = d_inode(dentry);
struct super_block *sb = inode->i_sb;
struct apfs_inode_info *ai = APFS_I(inode);
char *primary_name = NULL;
int err;
err = apfs_delete_dentry(dentry);
if (err)
if (err) {
apfs_err(sb, "failed to delete dentry recs for ino 0x%llx", apfs_ino(inode));
return err;
}
drop_nlink(inode);
if (!inode->i_nlink) {
@@ -1234,6 +1333,8 @@ static int __apfs_unlink(struct inode *dir, struct dentry *dentry)
inode->i_ctime = dir->i_ctime;
/* TODO: defer write of the primary name? */
err = apfs_update_inode(inode, primary_name);
if (err)
apfs_err(sb, "inode update failed for 0x%llx", apfs_ino(inode));
fail:
kfree(primary_name);
@@ -1288,10 +1389,14 @@ int apfs_rmdir(struct inode *dir, struct dentry *dentry)
int apfs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
unsigned int flags)
#else
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
int apfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
struct dentry *old_dentry, struct inode *new_dir,
struct dentry *new_dentry, unsigned int flags)
#else
int apfs_rename(struct mnt_idmap *idmap, struct inode *old_dir,
struct dentry *old_dentry, struct inode *new_dir,
struct dentry *new_dentry, unsigned int flags)
#endif
{
struct super_block *sb = old_dir->i_sb;
@@ -1317,17 +1422,23 @@ int apfs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
if (new_inode) {
err = __apfs_unlink(new_dir, new_dentry);
if (err)
if (err) {
apfs_err(sb, "unlink failed for replaced dentry");
goto out_abort;
}
}
err = __apfs_link(old_dentry, new_dentry);
if (err)
if (err) {
apfs_err(sb, "link failed for new dentry");
goto out_undo_unlink_new;
}
err = __apfs_unlink(old_dir, old_dentry);
if (err)
if (err) {
apfs_err(sb, "unlink failed for old dentry");
goto out_undo_link;
}
err = apfs_transaction_commit(sb);
if (err)
+1 -1
View File
@@ -1,5 +1,5 @@
PACKAGE_NAME="linux-apfs-rw"
PACKAGE_VERSION="0.3.0"
PACKAGE_VERSION="0.3.2"
BUILT_MODULE_NAME[0]="apfs"
DEST_MODULE_LOCATION[0]="/extra"
+980 -313
View File
File diff suppressed because it is too large. Load diff
+32 -8
View File
@@ -41,6 +41,12 @@ static vm_fault_t apfs_page_mkwrite(struct vm_fault *vmf)
goto out;
apfs_inode_join_transaction(sb, inode);
err = apfs_inode_create_exclusive_dstream(inode);
if (err) {
apfs_err(sb, "dstream creation failed for ino 0x%llx", apfs_ino(inode));
goto out_abort;
}
lock_page(page);
wait_for_stable_page(page);
if (page->mapping != inode->i_mapping) {
@@ -73,8 +79,10 @@ static vm_fault_t apfs_page_mkwrite(struct vm_fault *vmf)
unlock_page(page); /* XXX: race? */
err = block_page_mkwrite(vma, vmf, apfs_get_new_block);
if (err)
if (err) {
apfs_err(sb, "mkwrite failed for ino 0x%llx", apfs_ino(inode));
goto out_abort;
}
set_page_dirty(page);
/* An immediate commit would leave the page unlocked */
@@ -130,15 +138,28 @@ int apfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
}
const struct file_operations apfs_file_operations = {
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
.write_iter = generic_file_write_iter,
.mmap = apfs_file_mmap,
.open = generic_file_open,
.fsync = apfs_fsync,
.unlocked_ioctl = apfs_file_ioctl,
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
.write_iter = generic_file_write_iter,
.mmap = apfs_file_mmap,
.open = generic_file_open,
.fsync = apfs_fsync,
.unlocked_ioctl = apfs_file_ioctl,
.copy_file_range = generic_copy_file_range,
.remap_file_range = apfs_remap_file_range,
};
#if LINUX_VERSION_CODE == KERNEL_VERSION(5, 3, 0)
/*
* This is needed mainly to test clones with xfstests, so we only support the
* kernel version I use during testing. TODO: support all kernel versions.
*/
int apfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len)
{
return generic_block_fiemap(inode, fieinfo, start, len, apfs_get_block);
}
#endif
const struct inode_operations apfs_file_inode_operations = {
.getattr = apfs_getattr,
.listxattr = apfs_listxattr,
@@ -148,4 +169,7 @@ const struct inode_operations apfs_file_inode_operations = {
.fileattr_get = apfs_fileattr_get,
.fileattr_set = apfs_fileattr_set,
#endif
#if LINUX_VERSION_CODE == KERNEL_VERSION(5, 3, 0)
.fiemap = apfs_fiemap,
#endif
};
+448 -137
View File
File diff suppressed because it is too large. Load diff
+34 -10
View File
@@ -81,8 +81,10 @@ int apfs_keycmp(struct apfs_key *k1, struct apfs_key *k2)
*/
int apfs_read_cat_key(void *raw, int size, struct apfs_key *key, bool hashed)
{
if (size < sizeof(struct apfs_key_header))
if (size < sizeof(struct apfs_key_header)) {
apfs_err(NULL, "bad key length (%d)", size);
return -EFSCORRUPTED;
}
key->id = apfs_cat_cnid((struct apfs_key_header *)raw);
key->type = apfs_cat_type((struct apfs_key_header *)raw);
@@ -92,6 +94,7 @@ int apfs_read_cat_key(void *raw, int size, struct apfs_key *key, bool hashed)
if (size < sizeof(struct apfs_drec_hashed_key) + 1 ||
*((char *)raw + size - 1) != 0) {
/* Filename must have NULL-termination */
apfs_err(NULL, "invalid drec key (%d)", size);
return -EFSCORRUPTED;
}
/* Name length is not used in key comparisons, only the hash */
@@ -103,6 +106,7 @@ int apfs_read_cat_key(void *raw, int size, struct apfs_key *key, bool hashed)
if (size < sizeof(struct apfs_drec_key) + 1 ||
*((char *)raw + size - 1) != 0) {
/* Filename must have NULL-termination */
apfs_err(NULL, "invalid drec key (%d)", size);
return -EFSCORRUPTED;
}
/* There's no hash */
@@ -114,21 +118,26 @@ int apfs_read_cat_key(void *raw, int size, struct apfs_key *key, bool hashed)
if (size < sizeof(struct apfs_xattr_key) + 1 ||
*((char *)raw + size - 1) != 0) {
/* xattr name must have NULL-termination */
apfs_err(NULL, "invalid xattr key (%d)", size);
return -EFSCORRUPTED;
}
key->number = 0;
key->name = ((struct apfs_xattr_key *)raw)->name;
break;
case APFS_TYPE_FILE_EXTENT:
if (size != sizeof(struct apfs_file_extent_key))
if (size != sizeof(struct apfs_file_extent_key)) {
apfs_err(NULL, "bad key length (%d)", size);
return -EFSCORRUPTED;
}
key->number = le64_to_cpu(
((struct apfs_file_extent_key *)raw)->logical_addr);
key->name = NULL;
break;
case APFS_TYPE_SIBLING_LINK:
if (size != sizeof(struct apfs_sibling_link_key))
if (size != sizeof(struct apfs_sibling_link_key)) {
apfs_err(NULL, "bad key length (%d)", size);
return -EFSCORRUPTED;
}
key->number = le64_to_cpu(
((struct apfs_sibling_link_key *)raw)->sibling_id);
key->name = NULL;
@@ -146,8 +155,10 @@ int apfs_read_fext_key(void *raw, int size, struct apfs_key *key)
{
struct apfs_fext_tree_key *raw_key;
if (size != sizeof(*raw_key))
if (size != sizeof(*raw_key)) {
apfs_err(NULL, "bad key length (%d)", size);
return -EFSCORRUPTED;
}
raw_key = raw;
key->id = le64_to_cpu(raw_key->private_id);
@@ -169,8 +180,10 @@ int apfs_read_free_queue_key(void *raw, int size, struct apfs_key *key)
{
struct apfs_spaceman_free_queue_key *raw_key;
if (size < sizeof(struct apfs_spaceman_free_queue_key))
if (size < sizeof(struct apfs_spaceman_free_queue_key)) {
apfs_err(NULL, "bad key length (%d)", size);
return -EFSCORRUPTED;
}
raw_key = raw;
key->id = le64_to_cpu(raw_key->sfqk_xid);
@@ -191,8 +204,10 @@ int apfs_read_free_queue_key(void *raw, int size, struct apfs_key *key)
*/
int apfs_read_omap_key(void *raw, int size, struct apfs_key *key)
{
if (size < sizeof(struct apfs_omap_key))
if (size < sizeof(struct apfs_omap_key)) {
apfs_err(NULL, "bad key length (%d)", size);
return -EFSCORRUPTED;
}
key->id = le64_to_cpu(((struct apfs_omap_key *)raw)->ok_oid);
key->type = 0;
@@ -212,8 +227,10 @@ int apfs_read_omap_key(void *raw, int size, struct apfs_key *key)
*/
int apfs_read_extentref_key(void *raw, int size, struct apfs_key *key)
{
if (size != sizeof(struct apfs_phys_ext_key))
if (size != sizeof(struct apfs_phys_ext_key)) {
apfs_err(NULL, "bad key length (%d)", size);
return -EFSCORRUPTED;
}
key->id = apfs_cat_cnid((struct apfs_key_header *)raw);
key->type = apfs_cat_type((struct apfs_key_header *)raw);
key->number = 0;
@@ -223,21 +240,26 @@ int apfs_read_extentref_key(void *raw, int size, struct apfs_key *key)
int apfs_read_snap_meta_key(void *raw, int size, struct apfs_key *key)
{
if (size < sizeof(struct apfs_key_header))
if (size < sizeof(struct apfs_key_header)) {
apfs_err(NULL, "bad key length (%d)", size);
return -EFSCORRUPTED;
}
key->id = apfs_cat_cnid((struct apfs_key_header *)raw);
key->type = apfs_cat_type((struct apfs_key_header *)raw);
key->number = 0;
switch (key->type) {
case APFS_TYPE_SNAP_METADATA:
if (size != sizeof(struct apfs_snap_metadata_key))
if (size != sizeof(struct apfs_snap_metadata_key)) {
apfs_err(NULL, "bad key length (%d)", size);
return -EFSCORRUPTED;
}
key->name = NULL;
return 0;
case APFS_TYPE_SNAP_NAME:
if (size < sizeof(struct apfs_snap_name_key) + 1 || *((char *)raw + size - 1) != 0) {
/* snapshot name must have NULL-termination */
apfs_err(NULL, "invalid snap name key (%d)", size);
return -EFSCORRUPTED;
}
key->name = ((struct apfs_snap_name_key *)raw)->name;
@@ -251,8 +273,10 @@ int apfs_read_omap_snap_key(void *raw, int size, struct apfs_key *key)
{
__le64 *xid = NULL;
if (size != sizeof(*xid))
if (size != sizeof(*xid)) {
apfs_err(NULL, "bad key length (%d)", size);
return -EFSCORRUPTED;
}
xid = raw;
key->id = le64_to_cpup(xid);
+9 -2
View File
@@ -6,8 +6,9 @@
#include <linux/fs.h>
#include "apfs.h"
void apfs_msg(struct super_block *sb, const char *prefix, const char *fmt, ...)
void apfs_msg(struct super_block *sb, const char *prefix, const char *func, int line, const char *fmt, ...)
{
char *sb_id = NULL;
struct va_format vaf;
va_list args;
@@ -16,7 +17,13 @@ void apfs_msg(struct super_block *sb, const char *prefix, const char *fmt, ...)
vaf.fmt = fmt;
vaf.va = &args;
printk("%sAPFS (%s): %pV\n", prefix, sb->s_id, &vaf);
/* The superblock is not available to all callers */
sb_id = sb ? sb->s_id : "?";
if (func)
printk("%sAPFS (%s): %pV (%s:%d)\n", prefix, sb_id, &vaf, func, line);
else
printk("%sAPFS (%s): %pV\n", prefix, sb_id, &vaf);
va_end(args);
}
+10 -3
View File
@@ -18,13 +18,17 @@ static struct dentry *apfs_lookup(struct inode *dir, struct dentry *dentry,
return ERR_PTR(-ENAMETOOLONG);
err = apfs_inode_by_name(dir, &dentry->d_name, &ino);
if (err && err != -ENODATA)
if (err && err != -ENODATA) {
apfs_err(dir->i_sb, "inode lookup by name failed");
return ERR_PTR(err);
}
if (!err) {
inode = apfs_iget(dir->i_sb, ino);
if (IS_ERR(inode))
if (IS_ERR(inode)) {
apfs_err(dir->i_sb, "iget failed");
return ERR_CAST(inode);
}
}
return d_splice_alias(inode, dentry);
@@ -33,9 +37,12 @@ static struct dentry *apfs_lookup(struct inode *dir, struct dentry *dentry,
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
static int apfs_symlink(struct inode *dir, struct dentry *dentry,
const char *symname)
#else
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
static int apfs_symlink(struct user_namespace *mnt_userns, struct inode *dir,
struct dentry *dentry, const char *symname)
#else
static int apfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
struct dentry *dentry, const char *symname)
#endif
{
/* Symlink permissions don't mean anything and their value is fixed */
+225 -58
View File
File diff suppressed because it is too large. Load diff
+46 -11
View File
@@ -78,8 +78,10 @@ static int apfs_cpm_lookup_oid(struct super_block *sb,
u32 map_count = le32_to_cpu(cpm->cpm_count);
int i;
if (map_count > apfs_max_maps_per_block(sb))
if (map_count > apfs_max_maps_per_block(sb)) {
apfs_err(sb, "block has too many maps (%d)", map_count);
return -EFSCORRUPTED;
}
for (i = 0; i < map_count; ++i) {
struct apfs_checkpoint_mapping *map = &cpm->cpm_map[i];
@@ -214,14 +216,17 @@ int apfs_remove_cpoint_map(struct super_block *sb, u64 bno)
int err = 0;
bh = apfs_read_cpm_block(sb);
if (!bh)
if (!bh) {
apfs_err(sb, "failed to read cpm block");
return -EIO;
}
cpm = (struct apfs_checkpoint_map_phys *)bh->b_data;
apfs_assert_in_transaction(sb, &cpm->cpm_o);
/* TODO: multiple cpm blocks? */
cpm_count = le32_to_cpu(cpm->cpm_count);
if (cpm_count > apfs_max_maps_per_block(sb)) {
apfs_err(sb, "block has too many maps (%d)", cpm_count);
err = -EFSCORRUPTED;
goto fail;
}
@@ -240,6 +245,7 @@ int apfs_remove_cpoint_map(struct super_block *sb, u64 bno)
map->cpm_paddr = cpu_to_le64(apfs_data_index_to_bno(sb, curr_off - 1));
}
if (!bno_map) {
apfs_err(sb, "no mapping for bno 0x%llx", bno);
err = -EFSCORRUPTED;
goto fail;
}
@@ -269,8 +275,10 @@ struct buffer_head *apfs_read_ephemeral_object(struct super_block *sb, u64 oid)
u32 desc_len = le32_to_cpu(raw_sb->nx_xp_desc_len);
u32 i;
if (!desc_blks || !desc_len)
if (!desc_blks || !desc_len) {
apfs_err(sb, "no blocks in checkpoint descriptor area");
return ERR_PTR(-EFSCORRUPTED);
}
/* Last block in the area is superblock; the rest are mapping blocks */
for (i = 0; i < desc_len - 1; ++i) {
@@ -281,8 +289,10 @@ struct buffer_head *apfs_read_ephemeral_object(struct super_block *sb, u64 oid)
int err;
bh = apfs_sb_bread(sb, cpm_bno);
if (!bh)
if (!bh) {
apfs_err(sb, "failed to read cpm block");
return ERR_PTR(-EIO);
}
cpm = (struct apfs_checkpoint_map_phys *)bh->b_data;
err = apfs_cpm_lookup_oid(sb, cpm, oid, &obj_bno);
@@ -290,15 +300,20 @@ struct buffer_head *apfs_read_ephemeral_object(struct super_block *sb, u64 oid)
cpm = NULL;
if (err == -EAGAIN) /* Search the next mapping block */
continue;
if (err)
if (err) {
apfs_err(sb, "cpm lookup failed for oid 0x%llx", oid);
return ERR_PTR(err);
}
bh = apfs_sb_bread(sb, obj_bno);
if (!bh)
if (!bh) {
apfs_err(sb, "failed to read ephemeral block");
return ERR_PTR(-EIO);
}
return bh;
}
return ERR_PTR(-EFSCORRUPTED); /* The mapping is missing */
apfs_err(sb, "no mapping for oid 0x%llx", oid);
return ERR_PTR(-EFSCORRUPTED);
}
/**
@@ -315,6 +330,7 @@ struct buffer_head *apfs_read_ephemeral_object(struct super_block *sb, u64 oid)
struct buffer_head *apfs_read_object_block(struct super_block *sb, u64 bno, bool write, bool preserve)
{
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
struct apfs_superblock *vsb_raw = NULL;
struct buffer_head *bh, *new_bh;
struct apfs_obj_phys *obj;
u32 type;
@@ -324,13 +340,16 @@ struct buffer_head *apfs_read_object_block(struct super_block *sb, u64 bno, bool
ASSERT(write || !preserve);
bh = apfs_sb_bread(sb, bno);
if (!bh)
if (!bh) {
apfs_err(sb, "failed to read object block 0x%llx", bno);
return ERR_PTR(-EIO);
}
obj = (struct apfs_obj_phys *)bh->b_data;
type = le32_to_cpu(obj->o_type);
ASSERT(!(type & APFS_OBJ_EPHEMERAL));
if (nxi->nx_flags & APFS_CHECK_NODES && !apfs_obj_verify_csum(sb, bh)) {
apfs_err(sb, "bad checksum for object in block 0x%llx", bno);
err = -EFSBADCRC;
goto fail;
}
@@ -344,18 +363,34 @@ struct buffer_head *apfs_read_object_block(struct super_block *sb, u64 bno, bool
return bh;
err = apfs_spaceman_allocate_block(sb, &new_bno, true /* backwards */);
if (err)
if (err) {
apfs_err(sb, "block allocation failed");
goto fail;
}
new_bh = apfs_getblk(sb, new_bno);
if (!new_bh) {
apfs_err(sb, "failed to map block for CoW (0x%llx)", new_bno);
err = -EIO;
goto fail;
}
memcpy(new_bh->b_data, bh->b_data, sb->s_blocksize);
/* Don't free the old copy if it's part of a snapshot */
if (!preserve)
/*
* Don't free the old copy of the object if it's part of a snapshot.
* Also increase the allocation count, except for the volume superblock
* which is never counted there.
*/
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);
} 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);
le64_add_cpu(&vsb_raw->apfs_fs_alloc_count, 1);
le64_add_cpu(&vsb_raw->apfs_total_blocks_alloced, 1);
}
brelse(bh);
bh = new_bh;
new_bh = NULL;
+109 -39
View File
@@ -24,16 +24,18 @@ static int apfs_create_superblock_snapshot(struct super_block *sb, u64 *bno)
int err;
err = apfs_spaceman_allocate_block(sb, bno, true /* backwards */);
if (err)
if (err) {
apfs_err(sb, "block allocation failed");
goto fail;
}
snap_bh = apfs_getblk(sb, *bno);
if (!snap_bh) {
apfs_err(sb, "failed to map block for volume snap (0x%llx)", *bno);
err = -EIO;
goto fail;
}
apfs_assert_in_transaction(sb, &vsb_raw->apfs_o);
le64_add_cpu(&vsb_raw->apfs_fs_alloc_count, 1);
curr_bh = sbi->s_vobject.o_bh;
memcpy(snap_bh->b_data, curr_bh->b_data, sb->s_blocksize);
@@ -89,11 +91,14 @@ static int apfs_create_snap_metadata_rec(struct inode *mntpoint, struct apfs_nod
err = apfs_btree_query(sb, &query);
if (err == 0) {
apfs_err(sb, "record exists for xid 0x%llx", xid);
err = -EFSCORRUPTED;
goto fail;
}
if (err != -ENODATA)
if (err != -ENODATA) {
apfs_err(sb, "query failed for xid 0x%llx", xid);
goto fail;
}
apfs_key_set_hdr(APFS_TYPE_SNAP_METADATA, xid, &raw_key);
@@ -108,13 +113,18 @@ static int apfs_create_snap_metadata_rec(struct inode *mntpoint, struct apfs_nod
now = current_time(mntpoint);
raw_val->create_time = cpu_to_le64(timespec64_to_ns(&now));
raw_val->change_time = raw_val->create_time;
raw_val->inum = 0; /* TODO: what is this? */
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);
apfs_assert_in_transaction(sb, &vsb_raw->apfs_o);
raw_val->inum = vsb_raw->apfs_next_obj_id;
le64_add_cpu(&vsb_raw->apfs_next_obj_id, 1);
err = apfs_btree_insert(query, &raw_key, sizeof(raw_key), raw_val, val_len);
if (err)
apfs_err(sb, "insertion failed for xid 0x%llx", xid);
fail:
kfree(raw_val);
raw_val = NULL;
@@ -150,8 +160,10 @@ static int apfs_create_snap_name_rec(struct apfs_node *snap_root, const char *na
err = -EEXIST;
goto fail;
}
if (err != -ENODATA)
if (err != -ENODATA) {
apfs_err(sb, "query failed (%s)", name);
goto fail;
}
key_len = sizeof(*raw_key) + name_len + 1;
raw_key = kzalloc(key_len, GFP_KERNEL);
@@ -166,6 +178,8 @@ static int apfs_create_snap_name_rec(struct apfs_node *snap_root, const char *na
raw_val.snap_xid = cpu_to_le64(APFS_NXI(sb)->nx_xid);
err = apfs_btree_insert(query, raw_key, key_len, &raw_val, sizeof(raw_val));
if (err)
apfs_err(sb, "insertion failed (%s)", name);
fail:
kfree(raw_key);
raw_key = NULL;
@@ -174,30 +188,29 @@ fail:
return err;
}
static int apfs_create_snap_meta_records(struct inode *mntpoint, const char *name, u64 sblock_oid)
static int apfs_create_snap_meta_records(struct inode *mntpoint, const char *name, int name_len, u64 sblock_oid)
{
struct super_block *sb = mntpoint->i_sb;
struct apfs_superblock *vsb_raw = APFS_SB(sb)->s_vsb_raw;
struct apfs_node *snap_root = NULL;
size_t name_len;
int err;
snap_root = apfs_read_node(sb, le64_to_cpu(vsb_raw->apfs_snap_meta_tree_oid), APFS_OBJ_PHYSICAL, true /* write */);
if (IS_ERR(snap_root))
if (IS_ERR(snap_root)) {
apfs_err(sb, "failed to read snap meta root 0x%llx", le64_to_cpu(vsb_raw->apfs_snap_meta_tree_oid));
return PTR_ERR(snap_root);
}
apfs_assert_in_transaction(sb, &vsb_raw->apfs_o);
vsb_raw->apfs_snap_meta_tree_oid = cpu_to_le64(snap_root->object.oid);
name_len = strlen(name);
if (name_len > APFS_SNAP_MAX_NAMELEN) {
err = -EFSCORRUPTED;
err = apfs_create_snap_metadata_rec(mntpoint, snap_root, name, name_len, sblock_oid);
if (err) {
apfs_err(sb, "meta rec creation failed");
goto fail;
}
err = apfs_create_snap_metadata_rec(mntpoint, snap_root, name, name_len, sblock_oid);
if (err)
goto fail;
err = apfs_create_snap_name_rec(snap_root, name, name_len);
if (err)
apfs_err(sb, "name rec creation failed");
fail:
apfs_node_free(snap_root);
@@ -211,8 +224,10 @@ static int apfs_create_new_extentref_tree(struct super_block *sb)
int err;
err = apfs_make_empty_btree_root(sb, APFS_OBJECT_TYPE_BLOCKREFTREE, &oid);
if (err)
if (err) {
apfs_err(sb, "failed to make empty root");
return err;
}
apfs_assert_in_transaction(sb, &vsb_raw->apfs_o);
vsb_raw->apfs_extentref_tree_oid = cpu_to_le64(oid);
@@ -240,13 +255,17 @@ static int apfs_update_omap_snap_tree(struct super_block *sb, __le64 *oid_p)
/* An empty snapshots tree may not even have a root yet */
if (!oid) {
err = apfs_make_empty_btree_root(sb, APFS_OBJECT_TYPE_OMAP_SNAPSHOT, &oid);
if (err)
if (err) {
apfs_err(sb, "failed to make empty root");
return err;
}
}
root = apfs_read_node(sb, oid, APFS_OBJ_PHYSICAL, true /* write */);
if (IS_ERR(root))
if (IS_ERR(root)) {
apfs_err(sb, "failed to read omap snap root 0x%llx", oid);
return PTR_ERR(root);
}
oid = 0;
apfs_init_omap_snap_key(nxi->nx_xid, &key);
@@ -261,14 +280,19 @@ static int apfs_update_omap_snap_tree(struct super_block *sb, __le64 *oid_p)
err = apfs_btree_query(sb, &query);
if (err == 0) {
apfs_err(sb, "record exists for xid 0x%llx", nxi->nx_xid);
err = -EFSCORRUPTED;
goto fail;
}
if (err != -ENODATA)
if (err != -ENODATA) {
apfs_err(sb, "query failed for xid 0x%llx", nxi->nx_xid);
goto fail;
}
raw_key = cpu_to_le64(nxi->nx_xid);
err = apfs_btree_insert(query, &raw_key, sizeof(raw_key), &raw_val, sizeof(raw_val));
if (err)
apfs_err(sb, "insertion failed for xid 0x%llx", nxi->nx_xid);
*oid_p = cpu_to_le64(root->object.block_nr);
fail:
@@ -298,14 +322,18 @@ static int apfs_update_omap_snapshots(struct super_block *sb)
omap_blk = le64_to_cpu(vsb_raw->apfs_omap_oid);
bh = apfs_read_object_block(sb, omap_blk, true /* write */, false /* preserve */);
if (IS_ERR(bh))
if (IS_ERR(bh)) {
apfs_err(sb, "CoW failed for bno 0x%llx", omap_blk);
return PTR_ERR(bh);
}
omap = (struct apfs_omap_phys *)bh->b_data;
apfs_assert_in_transaction(sb, &omap->om_o);
le32_add_cpu(&omap->om_snap_count, 1);
omap->om_most_recent_snap = cpu_to_le64(xid);
err = apfs_update_omap_snap_tree(sb, &omap->om_snapshot_tree_oid);
if (err)
apfs_err(sb, "omap snap tree update failed");
omap = NULL;
brelse(bh);
@@ -320,7 +348,7 @@ static int apfs_update_omap_snapshots(struct super_block *sb)
*
* Returns 0 on success, or a negative error code in case of failure.
*/
static int apfs_do_ioc_take_snapshot(struct inode *mntpoint, const char *name)
static int apfs_do_ioc_take_snapshot(struct inode *mntpoint, const char *name, int name_len)
{
struct super_block *sb = mntpoint->i_sb;
struct apfs_sb_info *sbi = APFS_SB(sb);
@@ -341,24 +369,34 @@ static int apfs_do_ioc_take_snapshot(struct inode *mntpoint, const char *name)
* changes to the layout after the snapshot is set up.
*/
err = apfs_transaction_flush_all_inodes(sb);
if (err)
return err;
if (err) {
apfs_err(sb, "failed to flush all inodes");
goto fail;
}
err = apfs_create_superblock_snapshot(sb, &sblock_oid);
if (err)
return err;
err = apfs_create_snap_meta_records(mntpoint, name, sblock_oid);
if (err)
if (err) {
apfs_err(sb, "failed to snapshot superblock");
goto fail;
}
err = apfs_create_snap_meta_records(mntpoint, name, name_len, sblock_oid);
if (err) {
apfs_err(sb, "failed to create snap meta records");
goto fail;
}
err = apfs_create_new_extentref_tree(sb);
if (err)
if (err) {
apfs_err(sb, "failed to create new extref tree");
goto fail;
}
err = apfs_update_omap_snapshots(sb);
if (err)
if (err) {
apfs_err(sb, "failed to update omap snapshots");
goto fail;
}
/*
* The official reference allows old implementations to ignore extended
@@ -395,6 +433,7 @@ int apfs_ioc_take_snapshot(struct file *file, void __user *user_arg)
struct inode *inode = file_inode(file);
struct super_block *sb = inode->i_sb;
struct apfs_ioctl_snap_name *arg = NULL;
size_t name_len;
int err;
if (apfs_ino(inode) != APFS_ROOT_DIR_INO_NUM) {
@@ -404,8 +443,10 @@ int apfs_ioc_take_snapshot(struct file *file, void __user *user_arg)
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
if (!inode_owner_or_capable(inode))
#else
#elif LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0)
if (!inode_owner_or_capable(&init_user_ns, inode))
#else
if (!inode_owner_or_capable(&nop_mnt_idmap, inode))
#endif
return -EPERM;
@@ -424,7 +465,14 @@ int apfs_ioc_take_snapshot(struct file *file, void __user *user_arg)
goto fail;
}
err = apfs_do_ioc_take_snapshot(inode, arg->name);
name_len = strnlen(arg->name, sizeof(arg->name));
if (name_len == sizeof(arg->name)) {
apfs_warn(sb, "snapshot name is too long (%d)", (int)name_len);
err = -EINVAL;
goto fail;
}
err = apfs_do_ioc_take_snapshot(inode, arg->name, name_len);
fail:
kfree(arg);
arg = NULL;
@@ -437,8 +485,10 @@ static int apfs_snap_xid_from_query(struct apfs_query *query, u64 *xid)
char *raw = query->node->object.data;
__le64 *val = NULL;
if (query->len != sizeof(*val))
if (query->len != sizeof(*val)) {
apfs_err(query->node->object.sb, "bad value length (%d)", query->len);
return -EFSCORRUPTED;
}
val = (__le64 *)(raw + query->off);
*xid = le64_to_cpup(val);
@@ -461,10 +511,15 @@ static int apfs_snapshot_name_to_xid(struct apfs_node *snap_root, const char *na
query->flags |= APFS_QUERY_SNAP_META | APFS_QUERY_EXACT;
err = apfs_btree_query(sb, &query);
if (err)
if (err) {
if (err != -ENODATA)
apfs_err(sb, "query failed (%s)", name);
goto fail;
}
err = apfs_snap_xid_from_query(query, xid);
if (err)
apfs_err(sb, "bad snap name record (%s)", name);
fail:
apfs_free_query(query);
query = NULL;
@@ -476,8 +531,10 @@ static int apfs_snap_sblock_from_query(struct apfs_query *query, u64 *sblock_oid
char *raw = query->node->object.data;
struct apfs_snap_metadata_val *val = NULL;
if (query->len < sizeof(*val))
if (query->len < sizeof(*val)) {
apfs_err(query->node->object.sb, "bad value length (%d)", query->len);
return -EFSCORRUPTED;
}
val = (struct apfs_snap_metadata_val *)(raw + query->off);
*sblock_oid = le64_to_cpu(val->sblock_oid);
@@ -500,10 +557,14 @@ static int apfs_snapshot_xid_to_sblock(struct apfs_node *snap_root, u64 xid, u64
query->flags |= APFS_QUERY_SNAP_META | APFS_QUERY_EXACT;
err = apfs_btree_query(sb, &query);
if (err)
if (err) {
apfs_err(sb, "query failed for xid 0x%llx", xid);
goto fail;
}
err = apfs_snap_sblock_from_query(query, sblock_oid);
if (err)
apfs_err(sb, "bad snap meta record for xid 0x%llx", xid);
fail:
apfs_free_query(query);
query = NULL;
@@ -531,17 +592,24 @@ int apfs_switch_to_snapshot(struct super_block *sb)
ASSERT(sb->s_flags & SB_RDONLY);
name = sbi->s_snap_name;
if (strlen(name) > APFS_SNAP_MAX_NAMELEN)
if (strlen(name) > APFS_SNAP_MAX_NAMELEN) {
apfs_warn(sb, "snapshot name is too long");
return -EINVAL;
}
snap_root = apfs_read_node(sb, le64_to_cpu(vsb_raw->apfs_snap_meta_tree_oid), APFS_OBJ_PHYSICAL, false /* write */);
if (IS_ERR(snap_root))
if (IS_ERR(snap_root)) {
apfs_err(sb, "failed to read snap meta root 0x%llx", le64_to_cpu(vsb_raw->apfs_snap_meta_tree_oid));
return PTR_ERR(snap_root);
}
vsb_raw = NULL;
err = apfs_snapshot_name_to_xid(snap_root, name, &xid);
if (err)
if (err) {
if (err == -ENODATA)
apfs_info(sb, "no snapshot under that name (%s)", name);
goto fail;
}
sbi->s_snap_xid = xid;
err = apfs_snapshot_xid_to_sblock(snap_root, xid, &sblock_oid);
@@ -550,6 +618,8 @@ int apfs_switch_to_snapshot(struct super_block *sb)
apfs_unmap_volume_super(sb);
err = apfs_map_volume_super_bno(sb, sblock_oid, nxi->nx_flags & APFS_CHECK_NODES);
if (err)
apfs_err(sb, "failed to map volume block 0x%llx", sblock_oid);
fail:
apfs_node_free(snap_root);
+181 -34
View File
@@ -87,8 +87,10 @@ static int apfs_read_spaceman_dev(struct super_block *sb,
/* Check that all the cib addresses fit in the spaceman block */
if ((long long)spaceman->sm_addr_offset +
(long long)spaceman->sm_cib_count * sizeof(u64) > sb->s_blocksize)
(long long)spaceman->sm_cib_count * sizeof(u64) > sb->s_blocksize) {
apfs_err(sb, "too many cibs (%u)", spaceman->sm_cib_count);
return -EFSCORRUPTED;
}
return 0;
}
@@ -146,10 +148,14 @@ static int apfs_update_ip_bm_free_next(struct super_block *sb)
__le16 *free_next;
int i;
if (free_next_off > sb->s_blocksize)
if (free_next_off > sb->s_blocksize) {
apfs_err(sb, "offset out of bounds (%u)", free_next_off);
return -EFSCORRUPTED;
if (free_next_off + bmap_count * sizeof(*free_next) > sb->s_blocksize)
}
if (free_next_off + bmap_count * sizeof(*free_next) > sb->s_blocksize) {
apfs_err(sb, "free next out of bounds (%u-%u)", free_next_off, bmap_count * (u32)sizeof(*free_next));
return -EFSCORRUPTED;
}
free_next = (void *)raw + free_next_off;
for (i = 0; i < bmap_count; ++i) {
@@ -187,19 +193,25 @@ static int apfs_rotate_ip_bitmaps(struct super_block *sb)
}
xid = apfs_spaceman_get_64(sb, le32_to_cpu(sm_raw->sm_ip_bm_xid_offset));
if (!xid)
if (!xid) {
apfs_err(sb, "xid out of bounds (%u)", le32_to_cpu(sm_raw->sm_ip_bm_xid_offset));
return -EFSCORRUPTED;
}
*xid = cpu_to_le64(nxi->nx_xid);
free_head = le16_to_cpu(sm_raw->sm_ip_bm_free_head);
free_tail = le16_to_cpu(sm_raw->sm_ip_bm_free_tail);
curr_bmap_off = apfs_spaceman_get_64(sb, le32_to_cpu(sm_raw->sm_ip_bitmap_offset));
if (!curr_bmap_off)
if (!curr_bmap_off) {
apfs_err(sb, "bmap offset out of bounds (%u)", le32_to_cpu(sm_raw->sm_ip_bitmap_offset));
return -EFSCORRUPTED;
}
old_bh = apfs_sb_bread(sb, bmap_base + le64_to_cpup(curr_bmap_off));
if (!old_bh)
if (!old_bh) {
apfs_err(sb, "failed to read current ip bitmap (0x%llx)", bmap_base + le64_to_cpup(curr_bmap_off));
return -EIO;
}
*curr_bmap_off = cpu_to_le64(free_head);
free_head = (free_head + 1) % bmap_length;
@@ -207,11 +219,14 @@ static int apfs_rotate_ip_bitmaps(struct super_block *sb)
sm_raw->sm_ip_bm_free_head = cpu_to_le16(free_head);
sm_raw->sm_ip_bm_free_tail = cpu_to_le16(free_tail);
err = apfs_update_ip_bm_free_next(sb);
if (err)
if (err) {
apfs_err(sb, "failed to update bitmap ring");
goto out;
}
new_bh = apfs_getblk(sb, bmap_base + le64_to_cpup(curr_bmap_off));
if (!new_bh) {
apfs_err(sb, "failed to map block for CoW (0x%llx)", bmap_base + le64_to_cpup(curr_bmap_off));
err = -EIO;
goto out;
}
@@ -251,8 +266,10 @@ static int apfs_fq_rec_from_query(struct apfs_query *query, struct apfs_fq_rec *
char *raw = query->node->object.data;
struct apfs_spaceman_free_queue_key *key;
if (query->key_len != sizeof(*key))
if (query->key_len != sizeof(*key)) {
apfs_err(query->node->object.sb, "bad key length (%d)", query->key_len);
return -EFSCORRUPTED;
}
key = (struct apfs_spaceman_free_queue_key *)(raw + query->key_off);
fqrec->xid = le64_to_cpu(key->sfqk_xid);
@@ -265,6 +282,7 @@ static int apfs_fq_rec_from_query(struct apfs_query *query, struct apfs_fq_rec *
fqrec->len = le64_to_cpup((__le64 *)(raw + query->off));
return 0;
}
apfs_err(query->node->object.sb, "bad value length (%d)", query->len);
return -EFSCORRUPTED;
}
@@ -331,23 +349,32 @@ static int apfs_flush_fq_rec(struct apfs_node *root, u64 xid, u64 *len)
query->flags |= APFS_QUERY_FREE_QUEUE | APFS_QUERY_ANY_NUMBER | APFS_QUERY_EXACT;
err = apfs_btree_query(sb, &query);
if (err)
if (err) {
if (err != -ENODATA)
apfs_err(sb, "query failed for xid 0x%llx, paddr 0x%llx", xid, 0ULL);
goto fail;
}
err = apfs_fq_rec_from_query(query, &fqrec);
if (err)
if (err) {
apfs_err(sb, "bad free queue rec for xid 0x%llx", xid);
goto fail;
}
for (bno = fqrec.bno; bno < fqrec.bno + fqrec.len; ++bno) {
if(apfs_block_in_ip(sm, bno))
err = apfs_ip_mark_free(sb, bno);
else
err = apfs_main_free(sb, bno);
if(err)
if (err) {
apfs_err(sb, "freeing block 0x%llx failed (%d)", (unsigned long long)bno, err);
goto fail;
}
}
err = apfs_btree_remove(query);
if (err)
if (err) {
apfs_err(sb, "removal failed for xid 0x%llx", xid);
goto fail;
}
*len = fqrec.len;
fail:
@@ -391,8 +418,10 @@ static int apfs_flush_free_queue(struct super_block *sb, unsigned qid)
fq_root = apfs_read_node(sb, le64_to_cpu(fq->sfq_tree_oid),
APFS_OBJ_EPHEMERAL, true /* write */);
if (IS_ERR(fq_root))
if (IS_ERR(fq_root)) {
apfs_err(sb, "failed to read fq root 0x%llx", le64_to_cpu(fq->sfq_tree_oid));
return PTR_ERR(fq_root);
}
/* Preserve a few transactions */
while (oldest + 4 < nxi->nx_xid) {
@@ -407,6 +436,7 @@ static int apfs_flush_free_queue(struct super_block *sb, unsigned qid)
err = 0;
break;
} else if (err) {
apfs_err(sb, "failed to flush fq");
goto fail;
} else {
le64_add_cpu(&fq->sfq_count, -count);
@@ -458,9 +488,13 @@ int apfs_read_spaceman(struct super_block *sb)
if (sb->s_flags & SB_RDONLY) /* The space manager won't be needed */
return 0;
spaceman->sm_free_cache_base = spaceman->sm_free_cache_blkcnt = 0;
sm_bh = apfs_read_ephemeral_object(sb, oid);
if (IS_ERR(sm_bh))
if (IS_ERR(sm_bh)) {
apfs_err(sb, "ephemeral read failed for oid 0x%llx", oid);
return PTR_ERR(sm_bh);
}
sm_raw = (struct apfs_spaceman_phys *)sm_bh->b_data;
sm_flags = le32_to_cpu(sm_raw->sm_flags);
@@ -470,13 +504,16 @@ int apfs_read_spaceman(struct super_block *sb)
/* Only read the main device; fusion drives are not yet supported */
err = apfs_read_spaceman_dev(sb, &sm_raw->sm_dev[APFS_SD_MAIN]);
if (err)
if (err) {
apfs_err(sb, "failed to read main device");
goto fail;
}
spaceman->sm_blocks_per_chunk =
le32_to_cpu(sm_raw->sm_blocks_per_chunk);
spaceman->sm_chunks_per_cib = le32_to_cpu(sm_raw->sm_chunks_per_cib);
if (spaceman->sm_chunks_per_cib > apfs_max_chunks_per_cib(sb)) {
apfs_err(sb, "too many chunks per cib (%u)", spaceman->sm_chunks_per_cib);
err = -EFSCORRUPTED;
goto fail;
}
@@ -484,14 +521,20 @@ int apfs_read_spaceman(struct super_block *sb)
spaceman->sm_bh = sm_bh;
spaceman->sm_raw = sm_raw;
err = apfs_rotate_ip_bitmaps(sb);
if (err)
if (err) {
apfs_err(sb, "failed to rotate ip bitmaps");
goto fail;
}
err = apfs_flush_free_queue(sb, APFS_SFQ_IP);
if (err)
if (err) {
apfs_err(sb, "failed to flush ip fq");
goto fail;
}
err = apfs_flush_free_queue(sb, APFS_SFQ_MAIN);
if (err)
if (err) {
apfs_err(sb, "failed to flush main fq");
goto fail;
}
return 0;
fail:
@@ -607,24 +650,30 @@ static inline int apfs_chunk_mark_free(struct super_block *sb, char *bitmap,
}
/**
* apfs_free_queue_insert - Add a block range to its free queue
* apfs_free_queue_insert_nocache - Add a block range to its free queue
* @sb: superblock structure
* @bno: first block number to free
* @count: number of consecutive blocks to free
*
* Same as apfs_free_queue_insert(), but writes to the free queue directly,
* bypassing the cache of the latest freed block range.
*
* Returns 0 on success or a negative error code in case of failure.
*/
int apfs_free_queue_insert(struct super_block *sb, u64 bno, u64 count)
int apfs_free_queue_insert_nocache(struct super_block *sb, u64 bno, u64 count)
{
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
struct apfs_spaceman *sm = APFS_SM(sb);
struct apfs_spaceman_phys *sm_raw = sm->sm_raw;
struct apfs_spaceman_free_queue *fq;
struct apfs_node *fq_root;
struct apfs_node *fq_root = NULL;
struct apfs_btree_info *fq_info = NULL;
struct apfs_query *query = NULL;
struct apfs_spaceman_free_queue_key raw_key;
__le64 raw_val;
struct apfs_key key;
u64 node_count;
u16 node_limit;
int err;
if (apfs_block_in_ip(sm, bno))
@@ -634,8 +683,10 @@ int apfs_free_queue_insert(struct super_block *sb, u64 bno, u64 count)
fq_root = apfs_read_node(sb, le64_to_cpu(fq->sfq_tree_oid),
APFS_OBJ_EPHEMERAL, true /* write */);
if (IS_ERR(fq_root))
if (IS_ERR(fq_root)) {
apfs_err(sb, "failed to read fq root 0x%llx", le64_to_cpu(fq->sfq_tree_oid));
return PTR_ERR(fq_root);
}
query = apfs_alloc_query(fq_root, NULL /* parent */);
if (!query) {
@@ -648,8 +699,10 @@ int apfs_free_queue_insert(struct super_block *sb, u64 bno, u64 count)
query->flags |= APFS_QUERY_FREE_QUEUE;
err = apfs_btree_query(sb, &query);
if (err && err != -ENODATA)
if (err && err != -ENODATA) {
apfs_err(sb, "query failed for xid 0x%llx, paddr 0x%llx", nxi->nx_xid, bno);
goto fail;
}
raw_key.sfqk_xid = cpu_to_le64(nxi->nx_xid);
raw_key.sfqk_paddr = cpu_to_le64(bno);
@@ -660,8 +713,24 @@ int apfs_free_queue_insert(struct super_block *sb, u64 bno, u64 count)
raw_val = cpu_to_le64(count);
err = apfs_btree_insert(query, &raw_key, sizeof(raw_key), &raw_val, sizeof(raw_val));
}
if (err)
if (err) {
apfs_err(sb, "insertion failed for xid 0x%llx, paddr 0x%llx", nxi->nx_xid, bno);
goto fail;
}
fq_info = (void *)fq_root->object.data + sb->s_blocksize - sizeof(*fq_info);
node_count = le64_to_cpu(fq_info->bt_node_count);
node_limit = le16_to_cpu(fq->sfq_tree_node_limit);
if (node_count > node_limit) {
/*
* Ideally this should never happen, but at this point I can't
* be certain of that (TODO). If it does happen, it's best to
* abort and avoid corruption.
*/
apfs_alert(sb, "free queue has too many nodes (%llu > %u)", node_count, node_limit);
err = -EFSCORRUPTED;
goto fail;
}
if (!fq->sfq_oldest_xid)
fq->sfq_oldest_xid = cpu_to_le64(nxi->nx_xid);
@@ -674,6 +743,55 @@ fail:
return err;
}
/**
* apfs_free_queue_insert - Add a block range to its free queue
* @sb: superblock structure
* @bno: first block number to free
* @count: number of consecutive blocks to free
*
* Uses a cache to delay the actual tree operations as much as possible.
*
* Returns 0 on success or a negative error code in case of failure.
*/
int apfs_free_queue_insert(struct super_block *sb, u64 bno, u64 count)
{
struct apfs_spaceman *sm = APFS_SM(sb);
int err;
if (sm->sm_free_cache_base == 0) {
/* Nothing yet cached */
sm->sm_free_cache_base = bno;
sm->sm_free_cache_blkcnt = count;
return 0;
}
/*
* First attempt to extend the cache of freed blocks, but never cache
* a range that doesn't belong to a single free queue.
*/
if (apfs_block_in_ip(sm, bno) == apfs_block_in_ip(sm, sm->sm_free_cache_base)) {
if (bno == sm->sm_free_cache_base + sm->sm_free_cache_blkcnt) {
sm->sm_free_cache_blkcnt += count;
return 0;
}
if (bno + count == sm->sm_free_cache_base) {
sm->sm_free_cache_base -= count;
sm->sm_free_cache_blkcnt += count;
return 0;
}
}
/* Failed to extend the cache, so flush it and replace it */
err = apfs_free_queue_insert_nocache(sb, sm->sm_free_cache_base, sm->sm_free_cache_blkcnt);
if (err) {
apfs_err(sb, "fq cache flush failed (0x%llx-0x%llx)", sm->sm_free_cache_base, sm->sm_free_cache_blkcnt);
return err;
}
sm->sm_free_cache_base = bno;
sm->sm_free_cache_blkcnt = count;
return 0;
}
/**
* apfs_chunk_alloc_free - Allocate or free block in given CIB and chunk
* @sb: superblock structure
@@ -711,20 +829,25 @@ static int apfs_chunk_alloc_free(struct super_block *sb,
if (!ci->ci_bitmap_addr) {
u64 bmap_bno;
/* Freeing a block in an all-free chunk? */
if(!is_alloc)
if(!is_alloc) {
apfs_err(sb, "attempt to free block in all-free chunk");
return -EFSCORRUPTED;
}
/* All blocks in this chunk are free */
bmap_bno = apfs_ip_find_free(sb);
if (!bmap_bno)
if (!bmap_bno) {
apfs_err(sb, "no free blocks in ip");
return -EFSCORRUPTED;
}
bmap_bh = apfs_sb_bread(sb, bmap_bno);
} else {
bmap_bh = apfs_sb_bread(sb, le64_to_cpu(ci->ci_bitmap_addr));
}
if (!bmap_bh)
if (!bmap_bh) {
apfs_err(sb, "failed to read bitmap block");
return -EIO;
}
bmap = bmap_bh->b_data;
if (!ci->ci_bitmap_addr) {
memset(bmap, 0, sb->s_blocksize);
@@ -738,12 +861,14 @@ static int apfs_chunk_alloc_free(struct super_block *sb,
new_bmap_bno = apfs_ip_find_free(sb);
if (!new_bmap_bno) {
apfs_err(sb, "no free blocks in ip");
err = -EFSCORRUPTED;
goto fail;
}
new_bmap_bh = apfs_getblk(sb, new_bmap_bno);
if (!new_bmap_bh) {
apfs_err(sb, "failed to map new bmap block (0x%llx)", new_bmap_bno);
err = -EIO;
goto fail;
}
@@ -751,8 +876,10 @@ static int apfs_chunk_alloc_free(struct super_block *sb,
err = apfs_free_queue_insert(sb, bmap_bh->b_blocknr, 1);
brelse(bmap_bh);
bmap_bh = new_bmap_bh;
if (err)
if (err) {
apfs_err(sb, "free queue insertion failed");
goto fail;
}
bmap = bmap_bh->b_data;
}
apfs_ip_mark_used(sb, bmap_bh->b_blocknr);
@@ -764,12 +891,14 @@ static int apfs_chunk_alloc_free(struct super_block *sb,
new_cib_bno = apfs_ip_find_free(sb);
if (!new_cib_bno) {
apfs_err(sb, "no free blocks in ip");
err = -EFSCORRUPTED;
goto fail;
}
new_cib_bh = apfs_getblk(sb, new_cib_bno);
if (!new_cib_bh) {
apfs_err(sb, "failed to map new cib block (0x%llx)", new_cib_bno);
err = -EIO;
goto fail;
}
@@ -777,8 +906,10 @@ static int apfs_chunk_alloc_free(struct super_block *sb,
err = apfs_free_queue_insert(sb, (*cib_bh)->b_blocknr, 1);
brelse(*cib_bh);
*cib_bh = new_cib_bh;
if (err)
if (err) {
apfs_err(sb, "free queue insertion failed");
goto fail;
}
err = apfs_transaction_join(sb, *cib_bh);
if (err)
@@ -805,6 +936,7 @@ static int apfs_chunk_alloc_free(struct super_block *sb,
if(is_alloc) {
*bno = apfs_chunk_find_free(sb, bmap, le64_to_cpu(ci->ci_addr));
if (!*bno) {
apfs_err(sb, "no free blocks in chunk");
err = -EFSCORRUPTED;
goto fail;
}
@@ -812,6 +944,7 @@ static int apfs_chunk_alloc_free(struct super_block *sb,
sm->sm_free_count -= 1;
} else {
if(!apfs_chunk_mark_free(sb, bmap, *bno)) {
apfs_err(sb, "block already marked as free (0x%llx)", *bno);
le32_add_cpu(&ci->ci_free_count, -1);
set_buffer_csum(*cib_bh);
mark_buffer_dirty(*cib_bh);
@@ -872,8 +1005,10 @@ static int apfs_cib_allocate_block(struct super_block *sb,
/* Avoid out-of-bounds operations on corrupted cibs */
chunk_count = le32_to_cpu(cib->cib_chunk_info_count);
if (chunk_count > sm->sm_chunks_per_cib)
if (chunk_count > sm->sm_chunks_per_cib) {
apfs_err(sb, "too many chunks in cib (%u)", chunk_count);
return -EFSCORRUPTED;
}
for (i = 0; i < chunk_count; ++i) {
int index;
@@ -884,6 +1019,8 @@ static int apfs_cib_allocate_block(struct super_block *sb,
err = apfs_chunk_allocate_block(sb, cib_bh, index, bno);
if (err == -ENOSPC) /* This chunk is full */
continue;
if (err)
apfs_err(sb, "error during allocation");
return err;
}
return -ENOSPC;
@@ -914,8 +1051,10 @@ int apfs_spaceman_allocate_block(struct super_block *sb, u64 *bno, bool backward
cib_bno = apfs_spaceman_read_cib_addr(sb, index);
cib_bh = apfs_sb_bread(sb, cib_bno);
if (!cib_bh)
if (!cib_bh) {
apfs_err(sb, "failed to read cib");
return -EIO;
}
err = apfs_cib_allocate_block(sb, &cib_bh, bno, backwards);
if (!err) {
@@ -928,6 +1067,8 @@ int apfs_spaceman_allocate_block(struct super_block *sb, u64 *bno, bool backward
brelse(cib_bh);
if (err == -ENOSPC) /* This cib is full */
continue;
if (err)
apfs_err(sb, "error during allocation");
return err;
}
return -ENOSPC;
@@ -961,8 +1102,10 @@ static int apfs_main_free(struct super_block *sb, u64 bno)
u64 cib_bno;
int err;
if(!sm_raw->sm_blocks_per_chunk || !sm_raw->sm_chunks_per_cib)
if(!sm_raw->sm_blocks_per_chunk || !sm_raw->sm_chunks_per_cib) {
apfs_err(sb, "block or chunk count not set");
return -EINVAL;
}
/* TODO: use bitshifts instead of do_div() */
chunk_idx = bno;
do_div(chunk_idx, sm->sm_blocks_per_chunk);
@@ -971,8 +1114,10 @@ static int apfs_main_free(struct super_block *sb, u64 bno)
cib_bno = apfs_spaceman_read_cib_addr(sb, cib_idx);
cib_bh = apfs_sb_bread(sb, cib_bno);
if (!cib_bh)
if (!cib_bh) {
apfs_err(sb, "failed to read cib");
return -EIO;
}
err = apfs_chunk_free(sb, &cib_bh, chunk_idx, bno);
if (!err) {
@@ -983,6 +1128,8 @@ static int apfs_main_free(struct super_block *sb, u64 bno)
set_buffer_csum(sm->sm_bh);
}
brelse(cib_bh);
if (err)
apfs_err(sb, "error during free");
return err;
}
+1
View File
@@ -733,6 +733,7 @@ static struct inode *apfs_alloc_inode(struct super_block *sb)
ai->vfs_inode.i_version = 1;
#endif
dstream->ds_sb = sb;
dstream->ds_inode = &ai->vfs_inode;
dstream->ds_cached_ext.len = 0;
dstream->ds_ext_dirty = false;
ai->i_nchildren = 0;
+3 -2
View File
@@ -35,6 +35,7 @@ static const char *apfs_get_link(struct dentry *dentry, struct inode *inode,
size = __apfs_xattr_get(inode, APFS_XATTR_NAME_SYMLINK,
NULL /* buffer */, 0 /* size */);
if (size < 0) { /* TODO: return a better error code */
apfs_err(sb, "symlink size read failed");
err = size;
goto fail;
}
@@ -47,13 +48,13 @@ static const char *apfs_get_link(struct dentry *dentry, struct inode *inode,
size = __apfs_xattr_get(inode, APFS_XATTR_NAME_SYMLINK, target, size);
if (size < 0) {
apfs_err(sb, "symlink read failed");
err = size;
goto fail;
}
if (size == 0 || *(target + size - 1) != 0) {
/* Target path must be NULL-terminated */
apfs_alert(sb, "bad link target in inode 0x%llx",
apfs_ino(inode));
apfs_err(sb, "bad link target in inode 0x%llx", apfs_ino(inode));
err = -EFSCORRUPTED;
goto fail;
}
+68 -18
View File
@@ -95,13 +95,17 @@ static int apfs_cpoint_init_desc(struct super_block *sb)
u32 new_sb_index;
int err;
if (!desc_blks || !desc_len)
if (!desc_blks || !desc_len) {
apfs_err(sb, "bad checkpoint descriptor area");
return -EFSCORRUPTED;
}
err = apfs_cpoint_init_area(sb, desc_base, desc_blks,
desc_next, desc_len);
if (err)
if (err) {
apfs_err(sb, "failed to init area");
return err;
}
/* Now update the superblock with the new checkpoint */
@@ -149,13 +153,17 @@ static int apfs_cpoint_init_data(struct super_block *sb)
u32 data_len = le32_to_cpu(raw_sb->nx_xp_data_len);
int err;
if (!data_blks || !data_len)
if (!data_blks || !data_len) {
apfs_err(sb, "bad checkpoint data area");
return -EFSCORRUPTED;
}
err = apfs_cpoint_init_area(sb, data_base, data_blks,
data_next, data_len);
if (err)
if (err) {
apfs_err(sb, "failed to init area");
return err;
}
/* Apparently the previous checkpoint gets invalidated right away */
apfs_assert_in_transaction(sb, &raw_sb->nx_o);
@@ -219,14 +227,17 @@ static int apfs_update_mapping_blocks(struct super_block *sb)
int j;
bh = apfs_sb_bread(sb, desc_base + desc_curr);
if (!bh)
if (!bh) {
apfs_err(sb, "failed to read cpm block");
return -EINVAL;
}
ASSERT(buffer_trans(bh));
cpm = (struct apfs_checkpoint_map_phys *)bh->b_data;
apfs_assert_in_transaction(sb, &cpm->cpm_o);
map_count = le32_to_cpu(cpm->cpm_count);
if (map_count > apfs_max_maps_per_block(sb)) {
apfs_err(sb, "block has too many maps (%d)", map_count);
brelse(bh);
return -EFSCORRUPTED;
}
@@ -290,6 +301,7 @@ int apfs_cpoint_data_free(struct super_block *sb, u64 bno)
new_bh = apfs_getblk(sb, apfs_data_index_to_bno(sb, i));
old_bh = apfs_sb_bread(sb, apfs_data_index_to_bno(sb, i + 1));
if (!new_bh || !old_bh) {
apfs_err(sb, "failed to map blocks in data area");
brelse(new_bh);
brelse(old_bh);
return -EIO;
@@ -327,12 +339,18 @@ static int apfs_checkpoint_start(struct super_block *sb)
ASSERT(!(sb->s_flags & SB_RDONLY));
err = apfs_cpoint_init_desc(sb);
if (err)
if (err) {
apfs_err(sb, "failed to init descriptor area");
return err;
}
err = apfs_cpoint_init_data(sb);
if (err)
if (err) {
apfs_err(sb, "failed to init data area");
return err;
}
err = apfs_update_mapping_blocks(sb);
if (err)
apfs_err(sb, "failed to update checkpoint mappings");
return err;
}
@@ -444,12 +462,16 @@ int apfs_transaction_start(struct super_block *sb, struct apfs_max_ops maxops)
nx_trans->t_starts_count = 0;
err = apfs_checkpoint_start(sb);
if (err)
if (err) {
apfs_err(sb, "failed to start a new checkpoint");
goto fail;
}
err = apfs_read_spaceman(sb);
if (err)
if (err) {
apfs_err(sb, "failed to read the spaceman");
goto fail;
}
}
/* Don't start transactions unless we are sure they fit in disk */
@@ -457,8 +479,10 @@ int apfs_transaction_start(struct super_block *sb, struct apfs_max_ops maxops)
/* Commit what we have so far to flush the queues */
nx_trans->t_state |= APFS_NX_TRANS_FORCE_COMMIT;
err = apfs_transaction_commit(sb);
if (err)
if (err) {
apfs_err(sb, "commit failed");
goto fail;
}
return -ENOSPC;
}
@@ -473,16 +497,22 @@ int apfs_transaction_start(struct super_block *sb, struct apfs_max_ops maxops)
get_bh(vol_trans->t_old_omap_root.object.o_bh);
err = apfs_map_volume_super(sb, true /* write */);
if (err)
if (err) {
apfs_err(sb, "CoW failed for volume super");
goto fail;
}
/* TODO: don't copy these nodes for transactions that don't use them */
err = apfs_read_omap(sb, true /* write */);
if (err)
if (err) {
apfs_err(sb, "CoW failed for omap");
goto fail;
}
err = apfs_read_catalog(sb, true /* write */);
if (err)
if (err) {
apfs_err(sb, "Cow failed for catalog");
goto fail;
}
}
nx_trans->t_starts_count++;
@@ -577,8 +607,10 @@ int apfs_transaction_flush_all_inodes(struct super_block *sb)
nx_trans->t_state = 0;
/* Transaction aborted by ->evict_inode(), error code is lost */
if (sb->s_flags & SB_RDONLY)
if (sb->s_flags & SB_RDONLY) {
apfs_err(sb, "abort during inode eviction");
return -EROFS;
}
}
return err;
@@ -590,6 +622,7 @@ int apfs_transaction_flush_all_inodes(struct super_block *sb)
*/
static int apfs_transaction_commit_nx(struct super_block *sb)
{
struct apfs_spaceman *sm = APFS_SM(sb);
struct apfs_nxsb_info *nxi = APFS_NXI(sb);
struct apfs_sb_info *sbi;
struct apfs_nx_transaction *nx_trans = &nxi->nx_transaction;
@@ -601,8 +634,23 @@ static int apfs_transaction_commit_nx(struct super_block *sb)
/* Before committing the bhs, write all inode metadata to them */
err = apfs_transaction_flush_all_inodes(sb);
if (err)
if (err) {
apfs_err(sb, "failed to flush all inodes");
return err;
}
/*
* Now that nothing else will be freed, flush the last update to the
* free queues so that it can be committed to disk with the other bhs
*/
if (sm->sm_free_cache_base) {
err = apfs_free_queue_insert_nocache(sb, sm->sm_free_cache_base, sm->sm_free_cache_blkcnt);
if (err) {
apfs_err(sb, "fq cache flush failed (0x%llx-0x%llx)", sm->sm_free_cache_base, sm->sm_free_cache_blkcnt);
return err;
}
sm->sm_free_cache_base = sm->sm_free_cache_blkcnt = 0;
}
list_for_each_entry_safe(bhi, tmp, &nx_trans->t_buffers, list) {
struct buffer_head *bh = bhi->bh;
@@ -627,8 +675,10 @@ static int apfs_transaction_commit_nx(struct super_block *sb)
apfs_submit_bh(REQ_OP_WRITE, REQ_SYNC, bh);
}
err = apfs_checkpoint_end(sb);
if (err)
if (err) {
apfs_err(sb, "failed to end the checkpoint");
return err;
}
/* Success: forget the old container and volume superblocks */
brelse(nx_trans->t_old_msb);
@@ -738,7 +788,7 @@ int apfs_transaction_commit(struct super_block *sb)
if (apfs_transaction_need_commit(sb)) {
err = apfs_transaction_commit_nx(sb);
if (err) {
apfs_warn(sb, "transaction commit failed");
apfs_err(sb, "transaction commit failed");
return err;
}
}
@@ -846,7 +896,7 @@ void apfs_transaction_abort(struct super_block *sb)
ASSERT(nx_trans->t_old_msb);
nx_trans->t_state = 0;
apfs_warn(sb, "aborting transaction");
apfs_err(sb, "aborting transaction");
--nxi->nx_xid;
list_for_each_entry_safe(bhi, tmp, &nx_trans->t_buffers, list) {
Loaded 20 of 21 files, more files were not shown because too many files have changed in this diff. Show more