fs: ext4l: Support mounting a filesystem read-only
Fix several issues preventing ext4_fill_super() from completing: - Fix shrinker_alloc() to return a dummy shrinker instead of NULL - Fix getblk_unmovable() to use sb_getblk() instead of returning NULL - Fix mb_cache_create() to allocate a cache structure - Implement d_make_root() to create root dentry for mount - Add ext4_init_system_zone() call for block validity checking - Add global task_struct for journal_info consistency - Store super_block pointer after successful mount The filesystem now mounts successfully in read-only mode. Series-to: concept Series-links: 87 Cover-letter: fs: ext4l: Add support for mounting ext4 filesystems (part G) This series adds the ext4l filesystem driver, which is a port of the Linux ext4 driver to U-Boot. It allows mounting ext4 filesystems in read-only mode. The driver uses Linux kernel code directly where possible, with compatibility shims to handle differences between Linux and U-Boot. This approach makes it easier to keep the driver up to date with upstream changes. Key features: - Read-only mounting of ext4 filesystems - Support for extents, flex_bg, and other ext4 features - Buffer cache for improved performance - CRC32C checksums for metadata verification END Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com>
This commit is contained in:
@@ -58,16 +58,14 @@ struct timespec64 {
|
||||
};
|
||||
|
||||
/*
|
||||
* ktime_t, sector_t are in linux/types.h
|
||||
* atomic_t, atomic64_t are in asm-generic/atomic.h
|
||||
* MAX_JIFFY_OFFSET is in linux/jiffies.h
|
||||
* BDEVNAME_SIZE is in linux/blkdev.h
|
||||
* ktime_t, atomic_t, atomic64_t, sector_t are now in linux/types.h
|
||||
* MAX_JIFFY_OFFSET is now in linux/jiffies.h
|
||||
* BDEVNAME_SIZE is now in linux/blkdev.h
|
||||
*/
|
||||
#include <asm-generic/atomic.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/blkdev.h>
|
||||
|
||||
/* Extra atomic operations not in asm-generic/atomic.h */
|
||||
/* Extra atomic operation not in linux/types.h */
|
||||
#define atomic_dec_if_positive(v) (--(v)->counter)
|
||||
|
||||
/* SMP stubs - U-Boot is single-threaded */
|
||||
@@ -1138,7 +1136,10 @@ struct shrinker {
|
||||
static inline struct shrinker *shrinker_alloc(unsigned int flags,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
return NULL;
|
||||
/* Return static dummy - U-Boot doesn't need memory reclamation */
|
||||
static struct shrinker dummy_shrinker;
|
||||
|
||||
return &dummy_shrinker;
|
||||
}
|
||||
|
||||
static inline void shrinker_register(struct shrinker *s)
|
||||
@@ -1506,7 +1507,7 @@ static inline char *d_path(const struct path *path, char *buf, int buflen)
|
||||
#define filemap_splice_read(i, p, pi, l, f) ({ (void)(i); (void)(p); (void)(pi); (void)(l); (void)(f); 0L; })
|
||||
|
||||
/* Buffer operations - additional */
|
||||
#define getblk_unmovable(bd, b, s) ((struct buffer_head *)NULL)
|
||||
#define getblk_unmovable(bdev, block, size) sb_getblk(bdev->bd_super, block)
|
||||
#define create_empty_buffers(f, s, flags) ({ (void)(f); (void)(s); (void)(flags); (struct buffer_head *)NULL; })
|
||||
#define bh_offset(bh) (0UL)
|
||||
#define block_invalidate_folio(f, o, l) do { } while (0)
|
||||
@@ -2314,8 +2315,8 @@ struct mb_cache_entry {
|
||||
/* MB cache flags */
|
||||
#define MBE_REUSABLE_B 0
|
||||
|
||||
#define mb_cache_create(bits) ((struct mb_cache *)NULL)
|
||||
#define mb_cache_destroy(cache) do { (void)(cache); } while (0)
|
||||
#define mb_cache_create(bits) kzalloc(sizeof(struct mb_cache), GFP_KERNEL)
|
||||
#define mb_cache_destroy(cache) do { kfree(cache); } while (0)
|
||||
#define mb_cache_entry_find_first(c, h) ((struct mb_cache_entry *)NULL)
|
||||
#define mb_cache_entry_find_next(c, e) ((struct mb_cache_entry *)NULL)
|
||||
#define mb_cache_entry_delete_or_get(c, k, v) ((struct mb_cache_entry *)NULL)
|
||||
|
||||
@@ -28,6 +28,9 @@ static struct blk_desc *ext4l_blk_dev;
|
||||
static struct disk_partition ext4l_partition;
|
||||
static int ext4l_mounted;
|
||||
|
||||
/* Global super_block pointer for filesystem operations */
|
||||
static struct super_block *ext4l_sb;
|
||||
|
||||
/**
|
||||
* ext4l_get_blk_dev() - Get the current block device
|
||||
* Return: Block device descriptor or NULL if not mounted
|
||||
@@ -89,9 +92,7 @@ int ext4l_probe(struct blk_desc *fs_dev_desc,
|
||||
if (!fs_dev_desc)
|
||||
return -EINVAL;
|
||||
|
||||
/* Set up block device for buffer I/O */
|
||||
ext4l_set_blk_dev(fs_dev_desc, fs_partition);
|
||||
|
||||
/* Initialise CRC32C table for checksum verification */
|
||||
ext4l_crc32c_init();
|
||||
|
||||
/* Initialise journal subsystem if enabled */
|
||||
@@ -113,6 +114,11 @@ int ext4l_probe(struct blk_desc *fs_dev_desc,
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Initialise system zone for block validity checking */
|
||||
ret = ext4_init_system_zone();
|
||||
if (ret)
|
||||
goto err_exit_es;
|
||||
|
||||
/* Allocate super_block */
|
||||
sb = kzalloc(sizeof(struct super_block), GFP_KERNEL);
|
||||
if (!sb) {
|
||||
@@ -193,11 +199,18 @@ int ext4l_probe(struct blk_desc *fs_dev_desc,
|
||||
if (fs_partition)
|
||||
memcpy(&ext4l_part, fs_partition, sizeof(ext4l_part));
|
||||
|
||||
/* Set block device for buffer I/O */
|
||||
ext4l_set_blk_dev(fs_dev_desc, fs_partition);
|
||||
|
||||
/* Mount the filesystem */
|
||||
ret = ext4_fill_super(sb, fc);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
printf("ext4l: ext4_fill_super failed: %d\n", ret);
|
||||
goto err_free_ctx;
|
||||
}
|
||||
|
||||
/* Store super_block for later operations */
|
||||
ext4l_sb = sb;
|
||||
return 0;
|
||||
|
||||
err_free_buf:
|
||||
@@ -219,6 +232,7 @@ err_exit_es:
|
||||
|
||||
void ext4l_close(void)
|
||||
{
|
||||
ext4l_clear_blk_dev();
|
||||
ext4l_dev_desc = NULL;
|
||||
ext4l_sb = NULL;
|
||||
ext4l_clear_blk_dev();
|
||||
}
|
||||
|
||||
@@ -561,9 +561,30 @@ void generic_set_sb_d_ops(struct super_block *sb)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* d_make_root() - Create a root dentry for an inode
|
||||
* @inode: Inode to create dentry for
|
||||
* Return: Allocated dentry or NULL on failure
|
||||
*/
|
||||
struct dentry *d_make_root(struct inode *inode)
|
||||
{
|
||||
return NULL;
|
||||
struct dentry *de;
|
||||
|
||||
if (!inode)
|
||||
return NULL;
|
||||
|
||||
de = kzalloc(sizeof(struct dentry), GFP_KERNEL);
|
||||
if (!de) {
|
||||
iput(inode);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
de->d_inode = inode;
|
||||
de->d_sb = inode->i_sb;
|
||||
de->d_name.name = "/";
|
||||
de->d_name.len = 1;
|
||||
|
||||
return de;
|
||||
}
|
||||
|
||||
/* percpu init rwsem */
|
||||
|
||||
@@ -19,6 +19,13 @@
|
||||
#include "ext4_uboot.h"
|
||||
#include "ext4.h"
|
||||
|
||||
/*
|
||||
* Global task_struct for U-Boot.
|
||||
* This must be a single global instance shared across all translation units,
|
||||
* so that journal_info remains consistent.
|
||||
*/
|
||||
struct task_struct ext4l_current_task = { .comm = "u-boot", .pid = 1 };
|
||||
|
||||
/*
|
||||
* CRC32C support - uses Castagnoli polynomial 0x82F63B78
|
||||
* Table is initialised on first mount
|
||||
|
||||
@@ -165,6 +165,28 @@ typedef s64 ktime_t;
|
||||
typedef u64 sector_t;
|
||||
typedef u64 blkcnt_t;
|
||||
|
||||
/* Atomic types - stubs for single-threaded U-Boot */
|
||||
typedef struct {
|
||||
int counter;
|
||||
} atomic_t;
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
typedef struct {
|
||||
s64 counter;
|
||||
} atomic64_t;
|
||||
#else
|
||||
typedef struct {
|
||||
long counter;
|
||||
} atomic64_t;
|
||||
#endif
|
||||
|
||||
#define atomic_read(v) ((v)->counter)
|
||||
#define atomic_set(v, i) ((v)->counter = (i))
|
||||
#define atomic_inc(v) ((v)->counter++)
|
||||
#define atomic_dec(v) ((v)->counter--)
|
||||
#define atomic64_read(v) ((v)->counter)
|
||||
#define atomic64_set(v, i) ((v)->counter = (i))
|
||||
|
||||
#ifdef __linux__
|
||||
struct ustat {
|
||||
__kernel_daddr_t f_tfree;
|
||||
|
||||
Reference in New Issue
Block a user