Files

224 lines
5.8 KiB
C
Raw Permalink Normal View History

2021-11-16 18:36:02 -03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2018 Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>
*/
#include "apfs.h"
2024-01-17 16:41:37 -05:00
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
#include <linux/splice.h>
#endif
2021-09-13 13:02:18 -03:00
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)
typedef int vm_fault_t;
#endif
2025-04-17 20:56:05 -03:00
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 15, 0)
static void wait_for_stable_page(struct page *page)
{
return folio_wait_stable(page_folio(page));
}
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
static vm_fault_t apfs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
{
#else
2021-08-10 20:19:15 -03:00
static vm_fault_t apfs_page_mkwrite(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
#endif
2021-08-10 20:19:15 -03:00
struct page *page = vmf->page;
2023-11-11 10:51:36 -05:00
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
struct folio *folio;
#endif
struct inode *inode = file_inode(vma->vm_file);
2021-08-10 20:19:15 -03:00
struct super_block *sb = inode->i_sb;
struct buffer_head *bh, *head;
vm_fault_t ret = VM_FAULT_LOCKED;
unsigned int blocksize, block_start, len;
u64 size;
int err = 0;
sb_start_pagefault(inode->i_sb);
file_update_time(vma->vm_file);
2021-08-10 20:19:15 -03:00
2025-03-07 21:34:22 -03:00
err = apfs_transaction_start(sb, APFS_TRANS_REG);
2021-08-10 20:19:15 -03:00
if (err)
goto out;
apfs_inode_join_transaction(sb, inode);
err = apfs_inode_create_exclusive_dstream(inode);
2023-03-24 22:07:47 -03:00
if (err) {
apfs_err(sb, "dstream creation failed for ino 0x%llx", apfs_ino(inode));
goto out_abort;
2023-03-24 22:07:47 -03:00
}
2021-08-10 20:19:15 -03:00
lock_page(page);
wait_for_stable_page(page);
if (page->mapping != inode->i_mapping) {
ret = VM_FAULT_NOPAGE;
goto out_unlock;
}
2023-11-11 10:51:36 -05:00
if (!page_has_buffers(page)) {
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
2021-08-10 20:19:15 -03:00
create_empty_buffers(page, sb->s_blocksize, 0);
2023-11-11 10:51:36 -05:00
#else
folio = page_folio(page);
bh = folio_buffers(folio);
if (!bh)
bh = create_empty_buffers(folio, sb->s_blocksize, 0);
#endif
}
2021-08-10 20:19:15 -03:00
size = i_size_read(inode);
2025-07-01 20:45:23 -03:00
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 16, 0)
if (page_folio(page)->index == size >> PAGE_SHIFT)
#else
2021-08-10 20:19:15 -03:00
if (page->index == size >> PAGE_SHIFT)
2025-07-01 20:45:23 -03:00
#endif
2021-08-10 20:19:15 -03:00
len = size & ~PAGE_MASK;
else
len = PAGE_SIZE;
/* The blocks were read on the fault, mark them as unmapped for CoW */
head = page_buffers(page);
blocksize = head->b_size;
for (bh = head, block_start = 0; bh != head || !block_start;
block_start += blocksize, bh = bh->b_this_page) {
if (len > block_start) {
/* If it's not a hole, the fault read it already */
ASSERT(!buffer_mapped(bh) || buffer_uptodate(bh));
if (buffer_trans(bh))
continue;
clear_buffer_mapped(bh);
}
}
unlock_page(page); /* XXX: race? */
err = block_page_mkwrite(vma, vmf, apfs_get_new_block);
2023-03-24 22:07:47 -03:00
if (err) {
apfs_err(sb, "mkwrite failed for ino 0x%llx", apfs_ino(inode));
2021-08-10 20:19:15 -03:00
goto out_abort;
2023-03-24 22:07:47 -03:00
}
2021-08-10 20:19:15 -03:00
set_page_dirty(page);
/* An immediate commit would leave the page unlocked */
APFS_SB(sb)->s_nxi->nx_transaction.t_state |= APFS_NX_TRANS_DEFER_COMMIT;
2021-08-10 20:19:15 -03:00
err = apfs_transaction_commit(sb);
if (err)
goto out_unlock;
goto out;
out_unlock:
unlock_page(page);
out_abort:
apfs_transaction_abort(sb);
out:
if (err)
2025-03-03 18:09:28 -03:00
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) && !RHEL_VERSION_GE(9, 5)
2021-08-10 20:19:15 -03:00
ret = block_page_mkwrite_return(err);
2023-09-27 15:49:22 -04:00
#else
ret = vmf_fs_error(err);
#endif
2021-08-10 20:19:15 -03:00
sb_end_pagefault(inode->i_sb);
return ret;
}
static const struct vm_operations_struct apfs_file_vm_ops = {
.fault = filemap_fault,
.map_pages = filemap_map_pages,
.page_mkwrite = apfs_page_mkwrite,
};
2023-10-26 20:57:37 -03:00
int apfs_file_mmap(struct file *file, struct vm_area_struct *vma)
2021-08-10 20:19:15 -03:00
{
struct address_space *mapping = file->f_mapping;
2025-02-28 15:08:41 -03:00
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0) || RHEL_VERSION_GE(9, 3)
2022-08-14 16:45:57 -03:00
if (!mapping->a_ops->read_folio)
#else
2021-08-10 20:19:15 -03:00
if (!mapping->a_ops->readpage)
2022-08-14 16:45:57 -03:00
#endif
2021-08-10 20:19:15 -03:00
return -ENOEXEC;
file_accessed(file);
vma->vm_ops = &apfs_file_vm_ops;
return 0;
}
2021-08-16 22:59:18 -03:00
/*
* Just flush the whole transaction for now (TODO), since that's technically
* correct and easy to implement.
*/
int apfs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
struct inode *inode = file->f_mapping->host;
struct super_block *sb = inode->i_sb;
return apfs_sync_fs(sb, true /* wait */);
}
2024-01-17 16:41:37 -05:00
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
static ssize_t apfs_copy_file_range(struct file *src_file, loff_t src_off,
struct file *dst_file, loff_t dst_off,
size_t len, unsigned int flags)
{
return (splice_copy_file_range(src_file, src_off,
dst_file, dst_off, len));
}
#endif
const struct file_operations apfs_file_operations = {
2023-03-07 20:03:19 -03:00
.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,
2023-05-19 18:22:35 -03:00
2024-01-17 16:41:37 -05:00
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
.copy_file_range = apfs_copy_file_range,
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
2023-03-17 16:38:15 -03:00
.copy_file_range = generic_copy_file_range,
2023-05-19 18:22:35 -03:00
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
2023-03-07 20:03:19 -03:00
.remap_file_range = apfs_remap_file_range,
#else
.clone_file_range = apfs_clone_file_range,
#endif
2023-10-27 22:13:05 -03:00
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 5, 0)
.splice_read = generic_file_splice_read,
#else
.splice_read = filemap_splice_read,
#endif
.splice_write = iter_file_splice_write,
};
2023-03-22 16:11:10 -03:00
#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,
.setattr = apfs_setattr,
2021-07-21 17:02:18 -03:00
.update_time = apfs_update_time,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0)
.fileattr_get = apfs_fileattr_get,
.fileattr_set = apfs_fileattr_set,
#endif
2023-03-22 16:11:10 -03:00
#if LINUX_VERSION_CODE == KERNEL_VERSION(5, 3, 0)
.fiemap = apfs_fiemap,
#endif
};