From f16a76efc1de90ff4f71c970d7d4e0e42bb6ba7b Mon Sep 17 00:00:00 2001 From: Tulio A M Mendes Date: Fri, 17 Apr 2026 18:08:02 -0300 Subject: [PATCH] diskfs: fix LBA space leak on unlink/rmdir + add spinlock protection + ATA DMA irqsave Root cause: diskfs_unlink and diskfs_rmdir zeroed freed inodes but never reclaimed LBA space -- next_free_lba only grew, never shrank. After many test runs, next_free_lba exceeded the 8192-sector disk, causing ata_pio_read28 to fail with -EIO on out-of-range LBAs, making writes to newly created files fail silently. Fixes: - Add diskfs_reclaim_space() that recalculates next_free_lba by scanning all inodes for the highest used LBA extent. Called from both diskfs_unlink and diskfs_rmdir after freeing an inode. - Add g_diskfs_lock spinlock with irqsave/irqrestore variants protecting all superblock load/modify/store operations across all diskfs functions. All early returns properly unlock. - Restructure diskfs_read_impl and diskfs_write_impl to cache inode metadata under lock, then release before sector I/O, avoiding long interrupt-disabled periods that could cause deadlocks/timeouts. - Replace spin_lock/spin_unlock with spin_lock_irqsave/spin_unlock_ irqrestore in all 4 ATA DMA channel functions to prevent deadlocks from timer interrupts during lock hold. Test results: 103/103 PASS (3 consecutive runs, SMP=4), 16/16 battery PASS, 69/69 host PASS. --- src/hal/x86/ata_dma.c | 16 +- src/kernel/diskfs.c | 365 ++++++++++++++++++++++++++---------------- 2 files changed, 235 insertions(+), 146 deletions(-) diff --git a/src/hal/x86/ata_dma.c b/src/hal/x86/ata_dma.c index 08622a4b..91637b9c 100644 --- a/src/hal/x86/ata_dma.c +++ b/src/hal/x86/ata_dma.c @@ -296,10 +296,10 @@ int ata_dma_read28(int channel, int slave, uint32_t lba, uint8_t* buf512) { struct dma_ch_state* s = &dma_ch[channel]; if (!s->available) return -ENOSYS; - spin_lock(&s->lock); + uintptr_t irq_flags = spin_lock_irqsave(&s->lock); int ret = ata_dma_transfer(channel, slave, lba, 0); if (ret == 0) memcpy(buf512, s->dma_buf, 512); - spin_unlock(&s->lock); + spin_unlock_irqrestore(&s->lock, irq_flags); return ret; } @@ -309,14 +309,14 @@ int ata_dma_write28(int channel, int slave, uint32_t lba, const uint8_t* buf512) struct dma_ch_state* s = &dma_ch[channel]; if (!s->available) return -ENOSYS; - spin_lock(&s->lock); + uintptr_t irq_flags = spin_lock_irqsave(&s->lock); memcpy(s->dma_buf, buf512, 512); int ret = ata_dma_transfer(channel, slave, lba, 1); if (ret == 0) { outb((uint16_t)(ch_io[channel] + ATA_REG_COMMAND), ATA_CMD_CACHE_FLUSH); (void)ata_wait_not_busy_ch(channel); } - spin_unlock(&s->lock); + spin_unlock_irqrestore(&s->lock, irq_flags); return ret; } @@ -328,7 +328,7 @@ int ata_dma_read_direct(int channel, int slave, uint32_t lba, if (phys_buf == 0 || (phys_buf & 1)) return -EINVAL; if (byte_count == 0) byte_count = 512; - spin_lock(&s->lock); + uintptr_t irq_flags = spin_lock_irqsave(&s->lock); uint32_t saved_addr = s->prdt[0].phys_addr; uint16_t saved_count = s->prdt[0].byte_count; @@ -340,7 +340,7 @@ int ata_dma_read_direct(int channel, int slave, uint32_t lba, s->prdt[0].phys_addr = saved_addr; s->prdt[0].byte_count = saved_count; - spin_unlock(&s->lock); + spin_unlock_irqrestore(&s->lock, irq_flags); return ret; } @@ -352,7 +352,7 @@ int ata_dma_write_direct(int channel, int slave, uint32_t lba, if (phys_buf == 0 || (phys_buf & 1)) return -EINVAL; if (byte_count == 0) byte_count = 512; - spin_lock(&s->lock); + uintptr_t irq_flags = spin_lock_irqsave(&s->lock); uint32_t saved_addr = s->prdt[0].phys_addr; uint16_t saved_count = s->prdt[0].byte_count; @@ -369,6 +369,6 @@ int ata_dma_write_direct(int channel, int slave, uint32_t lba, s->prdt[0].phys_addr = saved_addr; s->prdt[0].byte_count = saved_count; - spin_unlock(&s->lock); + spin_unlock_irqrestore(&s->lock, irq_flags); return ret; } diff --git a/src/kernel/diskfs.c b/src/kernel/diskfs.c index 3072a90d..82b0f3e5 100644 --- a/src/kernel/diskfs.c +++ b/src/kernel/diskfs.c @@ -10,14 +10,17 @@ #include "diskfs.h" #include "ata_pio.h" +#include "console.h" #include "errno.h" #include "heap.h" +#include "spinlock.h" #include "utils.h" #include #include static int g_diskfs_drive = 0; +static spinlock_t g_diskfs_lock = {0}; // Very small on-disk FS stored starting at LBA2. // - LBA0 reserved @@ -331,14 +334,21 @@ static uint32_t diskfs_read_impl(fs_node_t* node, uint32_t offset, uint32_t size if (!g_ready) return 0; struct diskfs_node* dn = (struct diskfs_node*)node; + + /* Cache inode metadata under lock, then release before I/O */ + uintptr_t irq_flags = spin_lock_irqsave(&g_diskfs_lock); struct diskfs_super sb; - if (diskfs_super_load(&sb) < 0) return 0; - if (dn->ino >= DISKFS_MAX_INODES) return 0; - if (sb.inodes[dn->ino].type != DISKFS_INODE_FILE) return 0; + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return 0; } + if (dn->ino >= DISKFS_MAX_INODES) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return 0; } + if (sb.inodes[dn->ino].type != DISKFS_INODE_FILE) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return 0; } + + uint32_t start_lba = sb.inodes[dn->ino].start_lba; + uint32_t cap_sectors = sb.inodes[dn->ino].cap_sectors; + uint32_t size_bytes = sb.inodes[dn->ino].size_bytes; + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); - struct diskfs_inode* de = &sb.inodes[dn->ino]; - if (offset >= de->size_bytes) return 0; - if (offset + size > de->size_bytes) size = de->size_bytes - offset; + if (offset >= size_bytes) return 0; + if (offset + size > size_bytes) size = size_bytes - offset; if (size == 0) return 0; uint32_t total = 0; @@ -348,10 +358,10 @@ static uint32_t diskfs_read_impl(fs_node_t* node, uint32_t offset, uint32_t size uint32_t sec_off = pos % DISKFS_SECTOR; uint32_t chunk = size - total; if (chunk > (DISKFS_SECTOR - sec_off)) chunk = DISKFS_SECTOR - sec_off; - if (lba_off >= de->cap_sectors) break; + if (lba_off >= cap_sectors) break; uint8_t sec[DISKFS_SECTOR]; - if (ata_pio_read28(g_diskfs_drive, de->start_lba + lba_off, sec) < 0) break; + if (ata_pio_read28(g_diskfs_drive, start_lba + lba_off, sec) < 0) break; memcpy(buffer + total, sec + sec_off, chunk); total += chunk; } @@ -365,12 +375,6 @@ static uint32_t diskfs_write_impl(fs_node_t* node, uint32_t offset, uint32_t siz if (!g_ready) return 0; struct diskfs_node* dn = (struct diskfs_node*)node; - struct diskfs_super sb; - if (diskfs_super_load(&sb) < 0) return 0; - if (dn->ino >= DISKFS_MAX_INODES) return 0; - if (sb.inodes[dn->ino].type != DISKFS_INODE_FILE) return 0; - - struct diskfs_inode* de = &sb.inodes[dn->ino]; uint64_t end = (uint64_t)offset + (uint64_t)size; if (end > 0xFFFFFFFFULL) return 0; @@ -378,32 +382,57 @@ static uint32_t diskfs_write_impl(fs_node_t* node, uint32_t offset, uint32_t siz uint32_t need_bytes = (uint32_t)end; uint32_t need_sectors = (need_bytes + DISKFS_SECTOR - 1U) / DISKFS_SECTOR; - if (need_sectors > de->cap_sectors) { + /* Phase 1: Lock, load superblock, grow if needed, store, unlock */ + uint32_t start_lba; + uint32_t cap_sectors; + + uintptr_t irq_flags = spin_lock_irqsave(&g_diskfs_lock); + struct diskfs_super sb; + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return 0; } + if (dn->ino >= DISKFS_MAX_INODES) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return 0; } + if (sb.inodes[dn->ino].type != DISKFS_INODE_FILE) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return 0; } + + if (need_sectors > sb.inodes[dn->ino].cap_sectors) { // Grow by allocating a new extent at end, copy old contents. - uint32_t new_cap = de->cap_sectors; + uint32_t old_start = sb.inodes[dn->ino].start_lba; + uint32_t old_cap = sb.inodes[dn->ino].cap_sectors; + uint32_t new_cap = old_cap; while (new_cap < need_sectors) { new_cap *= 2U; - if (new_cap == 0) return 0; + if (new_cap == 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return 0; } } uint32_t new_start = sb.next_free_lba; sb.next_free_lba += new_cap; + sb.inodes[dn->ino].start_lba = new_start; + sb.inodes[dn->ino].cap_sectors = new_cap; + if (diskfs_super_store(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return 0; } + + /* Release lock for the data copy I/O */ + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); uint8_t sec[DISKFS_SECTOR]; for (uint32_t s = 0; s < new_cap; s++) { memset(sec, 0, sizeof(sec)); - if (s < de->cap_sectors) { - if (ata_pio_read28(g_diskfs_drive, de->start_lba + s, sec) < 0) { + if (s < old_cap) { + if (ata_pio_read28(g_diskfs_drive, old_start + s, sec) < 0) { return 0; } } (void)ata_pio_write28(g_diskfs_drive, new_start + s, sec); } - de->start_lba = new_start; - de->cap_sectors = new_cap; + /* Re-acquire lock to refresh cached metadata */ + irq_flags = spin_lock_irqsave(&g_diskfs_lock); + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return 0; } } + start_lba = sb.inodes[dn->ino].start_lba; + cap_sectors = sb.inodes[dn->ino].cap_sectors; + + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); + + /* Phase 2: Data I/O without lock (using cached start_lba/cap_sectors) */ uint32_t total = 0; while (total < size) { uint32_t pos = offset + total; @@ -411,27 +440,32 @@ static uint32_t diskfs_write_impl(fs_node_t* node, uint32_t offset, uint32_t siz uint32_t sec_off = pos % DISKFS_SECTOR; uint32_t chunk = size - total; if (chunk > (DISKFS_SECTOR - sec_off)) chunk = DISKFS_SECTOR - sec_off; - if (lba_off >= de->cap_sectors) break; + if (lba_off >= cap_sectors) break; uint8_t sec[DISKFS_SECTOR]; if (sec_off != 0 || chunk != DISKFS_SECTOR) { - if (ata_pio_read28(g_diskfs_drive, de->start_lba + lba_off, sec) < 0) break; + int rr = ata_pio_read28(g_diskfs_drive, start_lba + lba_off, sec); + if (rr < 0) break; } else { memset(sec, 0, sizeof(sec)); } memcpy(sec + sec_off, buffer + total, chunk); - if (ata_pio_write28(g_diskfs_drive, de->start_lba + lba_off, sec) < 0) break; + int wr = ata_pio_write28(g_diskfs_drive, start_lba + lba_off, sec); + if (wr < 0) break; total += chunk; } - if (offset + total > de->size_bytes) { - de->size_bytes = offset + total; + /* Phase 3: Lock, update size, store, unlock */ + irq_flags = spin_lock_irqsave(&g_diskfs_lock); + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return total; } + if (offset + total > sb.inodes[dn->ino].size_bytes) { + sb.inodes[dn->ino].size_bytes = offset + total; } - - if (diskfs_super_store(&sb) < 0) return total; - node->length = de->size_bytes; + node->length = sb.inodes[dn->ino].size_bytes; + (void)diskfs_super_store(&sb); + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return total; } @@ -443,6 +477,8 @@ static int diskfs_readdir_impl(struct fs_node* node, uint32_t* inout_index, void struct diskfs_node* dn = (struct diskfs_node*)node; uint16_t dir_ino = dn->ino; + uintptr_t irq_flags = spin_lock_irqsave(&g_diskfs_lock); + // Use diskfs_getdents which fills diskfs_kdirent; convert to vfs_dirent. struct diskfs_kdirent kbuf[8]; uint32_t klen = sizeof(kbuf); @@ -450,7 +486,7 @@ static int diskfs_readdir_impl(struct fs_node* node, uint32_t* inout_index, void uint32_t idx = *inout_index; int rc = diskfs_getdents(dir_ino, &idx, kbuf, klen); - if (rc <= 0) return rc; + if (rc <= 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return rc; } uint32_t nents = (uint32_t)rc / (uint32_t)sizeof(struct diskfs_kdirent); uint32_t cap = buf_len / (uint32_t)sizeof(struct vfs_dirent); @@ -466,10 +502,12 @@ static int diskfs_readdir_impl(struct fs_node* node, uint32_t* inout_index, void } *inout_index = idx; + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return (int)(nents * (uint32_t)sizeof(struct vfs_dirent)); } static int diskfs_vfs_truncate(struct fs_node* node, uint32_t length); +static void diskfs_reclaim_space(struct diskfs_super* sb); static struct fs_node* diskfs_root_finddir(struct fs_node* node, const char* name); static int diskfs_vfs_create(struct fs_node* dir, const char* name, uint32_t flags, struct fs_node** out); static int diskfs_vfs_mkdir(struct fs_node* dir, const char* name); @@ -512,17 +550,18 @@ static struct fs_node* diskfs_root_finddir(struct fs_node* node, const char* nam uint16_t parent_ino = parent ? parent->ino : 0; + uintptr_t irq_flags = spin_lock_irqsave(&g_diskfs_lock); struct diskfs_super sb; - if (diskfs_super_load(&sb) < 0) return NULL; - if (parent_ino >= DISKFS_MAX_INODES) return NULL; - if (sb.inodes[parent_ino].type != DISKFS_INODE_DIR) return NULL; + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return NULL; } + if (parent_ino >= DISKFS_MAX_INODES) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return NULL; } + if (sb.inodes[parent_ino].type != DISKFS_INODE_DIR) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return NULL; } int child = diskfs_find_child(&sb, parent_ino, name); - if (child < 0) return NULL; + if (child < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return NULL; } uint16_t cino = (uint16_t)child; struct diskfs_node* dn = (struct diskfs_node*)kmalloc(sizeof(*dn)); - if (!dn) return NULL; + if (!dn) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return NULL; } memset(dn, 0, sizeof(*dn)); strcpy(dn->vfs.name, name); @@ -541,6 +580,7 @@ static struct fs_node* diskfs_root_finddir(struct fs_node* node, const char* nam dn->vfs.i_ops = &diskfs_file_iops; } + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return &dn->vfs; } @@ -550,8 +590,9 @@ int diskfs_open_file(const char* rel_path, uint32_t flags, fs_node_t** out_node) if (!g_ready) return -ENODEV; if (!rel_path || rel_path[0] == 0) return -EINVAL; + uintptr_t irq_flags = spin_lock_irqsave(&g_diskfs_lock); struct diskfs_super sb; - if (diskfs_super_load(&sb) < 0) return -EIO; + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } uint16_t ino = 0; uint16_t parent = 0; @@ -559,34 +600,35 @@ int diskfs_open_file(const char* rel_path, uint32_t flags, fs_node_t** out_node) last[0] = 0; int rc = diskfs_lookup_path(&sb, rel_path, &ino, &parent, last, sizeof(last)); if (rc == -ENOENT) { - if ((flags & 0x40U) == 0U) return -ENOENT; // O_CREAT - if (last[0] == 0) return -EINVAL; + if ((flags & 0x40U) == 0U) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOENT; } // O_CREAT + if (last[0] == 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EINVAL; } // Ensure intermediate dirs exist: lookup again but stop before last segment. // We already have parent inode from lookup_path failure. - if (parent >= DISKFS_MAX_INODES) return -EIO; - if (sb.inodes[parent].type != DISKFS_INODE_DIR) return -ENOTDIR; + if (parent >= DISKFS_MAX_INODES) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } + if (sb.inodes[parent].type != DISKFS_INODE_DIR) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOTDIR; } uint16_t new_ino = 0; rc = diskfs_alloc_inode_file(&sb, parent, last, DISKFS_DEFAULT_CAP_SECTORS, &new_ino); - if (rc < 0) return rc; - if (diskfs_super_store(&sb) < 0) return -EIO; + if (rc < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return rc; } + if (diskfs_super_store(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } ino = new_ino; } else if (rc < 0) { + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return rc; } - if (ino >= DISKFS_MAX_INODES) return -EIO; - if (sb.inodes[ino].type != DISKFS_INODE_FILE) return -EISDIR; + if (ino >= DISKFS_MAX_INODES) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } + if (sb.inodes[ino].type != DISKFS_INODE_FILE) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EISDIR; } if ((flags & 0x200U) != 0U) { // O_TRUNC sb.inodes[ino].size_bytes = 0; - if (diskfs_super_store(&sb) < 0) return -EIO; + if (diskfs_super_store(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } } // Build a transient vfs node for this inode. struct diskfs_node* dn = (struct diskfs_node*)kmalloc(sizeof(*dn)); - if (!dn) return -ENOMEM; + if (!dn) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOMEM; } memset(dn, 0, sizeof(*dn)); diskfs_strlcpy(dn->vfs.name, last, sizeof(dn->vfs.name)); dn->vfs.flags = FS_FILE; @@ -597,6 +639,7 @@ int diskfs_open_file(const char* rel_path, uint32_t flags, fs_node_t** out_node) dn->ino = ino; *out_node = &dn->vfs; + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return 0; } @@ -604,8 +647,9 @@ int diskfs_mkdir(const char* rel_path) { if (!g_ready) return -ENODEV; if (!rel_path || rel_path[0] == 0) return -EINVAL; + uintptr_t irq_flags = spin_lock_irqsave(&g_diskfs_lock); struct diskfs_super sb; - if (diskfs_super_load(&sb) < 0) return -EIO; + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } uint16_t ino = 0; uint16_t parent = 0; @@ -613,13 +657,14 @@ int diskfs_mkdir(const char* rel_path) { last[0] = 0; int rc = diskfs_lookup_path(&sb, rel_path, &ino, &parent, last, sizeof(last)); if (rc == 0) { + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EEXIST; } - if (rc != -ENOENT) return rc; - if (last[0] == 0) return -EINVAL; + if (rc != -ENOENT) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return rc; } + if (last[0] == 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EINVAL; } - if (parent >= DISKFS_MAX_INODES) return -EIO; - if (sb.inodes[parent].type != DISKFS_INODE_DIR) return -ENOTDIR; + if (parent >= DISKFS_MAX_INODES) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } + if (sb.inodes[parent].type != DISKFS_INODE_DIR) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOTDIR; } for (uint16_t i = 1; i < DISKFS_MAX_INODES; i++) { if (sb.inodes[i].type != DISKFS_INODE_FREE) continue; @@ -630,9 +675,12 @@ int diskfs_mkdir(const char* rel_path) { sb.inodes[i].start_lba = 0; sb.inodes[i].size_bytes = 0; sb.inodes[i].cap_sectors = 0; - return diskfs_super_store(&sb); + int ret = diskfs_super_store(&sb); + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); + return ret; } + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOSPC; } @@ -640,35 +688,39 @@ int diskfs_unlink(const char* rel_path) { if (!g_ready) return -ENODEV; if (!rel_path || rel_path[0] == 0) return -EINVAL; + uintptr_t irq_flags = spin_lock_irqsave(&g_diskfs_lock); struct diskfs_super sb; - if (diskfs_super_load(&sb) < 0) return -EIO; + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } uint16_t ino = 0; int rc = diskfs_lookup_path(&sb, rel_path, &ino, NULL, NULL, 0); - if (rc < 0) return rc; - if (ino == 0) return -EPERM; - if (ino >= DISKFS_MAX_INODES) return -EIO; + if (rc < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return rc; } + if (ino == 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EPERM; } + if (ino >= DISKFS_MAX_INODES) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } - if (sb.inodes[ino].type == DISKFS_INODE_DIR) return -EISDIR; - if (sb.inodes[ino].type != DISKFS_INODE_FILE) return -ENOENT; + if (sb.inodes[ino].type == DISKFS_INODE_DIR) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EISDIR; } + if (sb.inodes[ino].type != DISKFS_INODE_FILE) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOENT; } - uint32_t shared_lba = sb.inodes[ino].start_lba; memset(&sb.inodes[ino], 0, sizeof(sb.inodes[ino])); - /* Check if any other inode still references the same data blocks */ - int still_referenced = 0; - if (shared_lba != 0) { - for (uint16_t i = 0; i < DISKFS_MAX_INODES; i++) { - if (sb.inodes[i].type == DISKFS_INODE_FILE && - sb.inodes[i].start_lba == shared_lba) { - still_referenced = 1; - break; - } - } - } - (void)still_referenced; /* data blocks are never reclaimed in current impl */ + /* Reclaim LBA space */ + diskfs_reclaim_space(&sb); - return diskfs_super_store(&sb); + int ret = diskfs_super_store(&sb); + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); + return ret; +} + +static void diskfs_reclaim_space(struct diskfs_super* sb) { + /* Recalculate next_free_lba by finding the highest used LBA extent */ + uint32_t highest_end = DISKFS_LBA_DATA_START; + for (uint16_t i = 0; i < DISKFS_MAX_INODES; i++) { + if (sb->inodes[i].type == DISKFS_INODE_FREE) continue; + if (sb->inodes[i].cap_sectors == 0) continue; + uint32_t end = sb->inodes[i].start_lba + sb->inodes[i].cap_sectors; + if (end > highest_end) highest_end = end; + } + sb->next_free_lba = highest_end; } int diskfs_link(const char* old_rel, const char* new_rel) { @@ -676,14 +728,15 @@ int diskfs_link(const char* old_rel, const char* new_rel) { if (!old_rel || old_rel[0] == 0) return -EINVAL; if (!new_rel || new_rel[0] == 0) return -EINVAL; + uintptr_t irq_flags = spin_lock_irqsave(&g_diskfs_lock); struct diskfs_super sb; - if (diskfs_super_load(&sb) < 0) return -EIO; + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } uint16_t old_ino = 0; int rc = diskfs_lookup_path(&sb, old_rel, &old_ino, NULL, NULL, 0); - if (rc < 0) return rc; - if (old_ino >= DISKFS_MAX_INODES) return -EIO; - if (sb.inodes[old_ino].type != DISKFS_INODE_FILE) return -EPERM; + if (rc < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return rc; } + if (old_ino >= DISKFS_MAX_INODES) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } + if (sb.inodes[old_ino].type != DISKFS_INODE_FILE) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EPERM; } /* Find the new name's parent directory and base name */ const char* new_base = new_rel; @@ -695,14 +748,14 @@ int diskfs_link(const char* old_rel, const char* new_rel) { /* Check new name doesn't already exist */ uint16_t dummy = 0; - if (diskfs_lookup_path(&sb, new_rel, &dummy, NULL, NULL, 0) == 0) return -EEXIST; + if (diskfs_lookup_path(&sb, new_rel, &dummy, NULL, NULL, 0) == 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EEXIST; } /* Find a free inode slot */ uint16_t new_ino = 0; for (uint16_t i = 1; i < DISKFS_MAX_INODES; i++) { if (sb.inodes[i].type == DISKFS_INODE_FREE) { new_ino = i; break; } } - if (new_ino == 0) return -ENOSPC; + if (new_ino == 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOSPC; } /* Create new inode sharing same data blocks */ sb.inodes[new_ino].type = DISKFS_INODE_FILE; @@ -717,31 +770,40 @@ int diskfs_link(const char* old_rel, const char* new_rel) { if (sb.inodes[old_ino].nlink < 2) sb.inodes[old_ino].nlink = 2; else sb.inodes[old_ino].nlink++; - return diskfs_super_store(&sb); + int ret = diskfs_super_store(&sb); + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); + return ret; } int diskfs_rmdir(const char* rel_path) { if (!g_ready) return -ENODEV; if (!rel_path || rel_path[0] == 0) return -EINVAL; + uintptr_t irq_flags = spin_lock_irqsave(&g_diskfs_lock); struct diskfs_super sb; - if (diskfs_super_load(&sb) < 0) return -EIO; + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } uint16_t ino = 0; int rc = diskfs_lookup_path(&sb, rel_path, &ino, NULL, NULL, 0); - if (rc < 0) return rc; - if (ino == 0) return -EPERM; - if (ino >= DISKFS_MAX_INODES) return -EIO; - if (sb.inodes[ino].type != DISKFS_INODE_DIR) return -ENOTDIR; + if (rc < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return rc; } + if (ino == 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EPERM; } + if (ino >= DISKFS_MAX_INODES) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } + if (sb.inodes[ino].type != DISKFS_INODE_DIR) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOTDIR; } // Check directory is empty (no children). for (uint16_t i = 0; i < DISKFS_MAX_INODES; i++) { if (sb.inodes[i].type == DISKFS_INODE_FREE) continue; - if (sb.inodes[i].parent == ino && i != ino) return -ENOTEMPTY; + if (sb.inodes[i].parent == ino && i != ino) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOTEMPTY; } } memset(&sb.inodes[ino], 0, sizeof(sb.inodes[ino])); - return diskfs_super_store(&sb); + + /* Reclaim LBA space */ + diskfs_reclaim_space(&sb); + + int ret = diskfs_super_store(&sb); + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); + return ret; } int diskfs_rename(const char* old_rel, const char* new_rel) { @@ -749,14 +811,15 @@ int diskfs_rename(const char* old_rel, const char* new_rel) { if (!old_rel || old_rel[0] == 0) return -EINVAL; if (!new_rel || new_rel[0] == 0) return -EINVAL; + uintptr_t irq_flags = spin_lock_irqsave(&g_diskfs_lock); struct diskfs_super sb; - if (diskfs_super_load(&sb) < 0) return -EIO; + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } uint16_t src_ino = 0; int rc = diskfs_lookup_path(&sb, old_rel, &src_ino, NULL, NULL, 0); - if (rc < 0) return rc; - if (src_ino == 0) return -EPERM; - if (src_ino >= DISKFS_MAX_INODES) return -EIO; + if (rc < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return rc; } + if (src_ino == 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EPERM; } + if (src_ino >= DISKFS_MAX_INODES) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } // Resolve destination: if it exists, it must be same type or we fail. uint16_t dst_ino = 0; @@ -766,15 +829,16 @@ int diskfs_rename(const char* old_rel, const char* new_rel) { rc = diskfs_lookup_path(&sb, new_rel, &dst_ino, &dst_parent, dst_last, sizeof(dst_last)); if (rc == 0) { // Destination exists: if it's a dir and source is file (or vice-versa), error. - if (sb.inodes[dst_ino].type != sb.inodes[src_ino].type) return -EINVAL; - if (dst_ino == src_ino) return 0; // same inode + if (sb.inodes[dst_ino].type != sb.inodes[src_ino].type) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EINVAL; } + if (dst_ino == src_ino) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return 0; } // same inode // Remove destination. memset(&sb.inodes[dst_ino], 0, sizeof(sb.inodes[dst_ino])); } else if (rc == -ENOENT) { // Parent must exist and be a dir. - if (dst_parent >= DISKFS_MAX_INODES) return -EIO; - if (sb.inodes[dst_parent].type != DISKFS_INODE_DIR) return -ENOTDIR; + if (dst_parent >= DISKFS_MAX_INODES) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } + if (sb.inodes[dst_parent].type != DISKFS_INODE_DIR) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOTDIR; } } else { + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return rc; } @@ -783,7 +847,9 @@ int diskfs_rename(const char* old_rel, const char* new_rel) { memset(sb.inodes[src_ino].name, 0, sizeof(sb.inodes[src_ino].name)); diskfs_strlcpy(sb.inodes[src_ino].name, dst_last, sizeof(sb.inodes[src_ino].name)); - return diskfs_super_store(&sb); + int ret = diskfs_super_store(&sb); + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); + return ret; } int diskfs_getdents(uint16_t dir_ino, uint32_t* inout_index, void* out, uint32_t out_len) { @@ -855,24 +921,25 @@ static int diskfs_vfs_create(struct fs_node* dir, const char* name, uint32_t fla struct diskfs_node* parent = (struct diskfs_node*)dir; uint16_t parent_ino = parent->ino; + uintptr_t irq_flags = spin_lock_irqsave(&g_diskfs_lock); struct diskfs_super sb; - if (diskfs_super_load(&sb) < 0) return -EIO; - if (parent_ino >= DISKFS_MAX_INODES) return -EIO; - if (sb.inodes[parent_ino].type != DISKFS_INODE_DIR) return -ENOTDIR; + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } + if (parent_ino >= DISKFS_MAX_INODES) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } + if (sb.inodes[parent_ino].type != DISKFS_INODE_DIR) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOTDIR; } /* Check if it already exists */ int existing = diskfs_find_child(&sb, parent_ino, name); if (existing >= 0) { uint16_t ino = (uint16_t)existing; - if (sb.inodes[ino].type != DISKFS_INODE_FILE) return -EISDIR; + if (sb.inodes[ino].type != DISKFS_INODE_FILE) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EISDIR; } if ((flags & 0x200U) != 0U) { /* O_TRUNC */ sb.inodes[ino].size_bytes = 0; - if (diskfs_super_store(&sb) < 0) return -EIO; + if (diskfs_super_store(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } } struct diskfs_node* dn = (struct diskfs_node*)kmalloc(sizeof(*dn)); - if (!dn) return -ENOMEM; + if (!dn) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOMEM; } memset(dn, 0, sizeof(*dn)); diskfs_strlcpy(dn->vfs.name, name, sizeof(dn->vfs.name)); dn->vfs.flags = FS_FILE; @@ -882,19 +949,20 @@ static int diskfs_vfs_create(struct fs_node* dir, const char* name, uint32_t fla dn->vfs.i_ops = &diskfs_file_iops; dn->ino = ino; *out = &dn->vfs; + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return 0; } /* Create new file */ - if ((flags & 0x40U) == 0U) return -ENOENT; /* O_CREAT not set */ + if ((flags & 0x40U) == 0U) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOENT; } /* O_CREAT not set */ uint16_t new_ino = 0; int rc = diskfs_alloc_inode_file(&sb, parent_ino, name, DISKFS_DEFAULT_CAP_SECTORS, &new_ino); - if (rc < 0) return rc; - if (diskfs_super_store(&sb) < 0) return -EIO; + if (rc < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return rc; } + if (diskfs_super_store(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } struct diskfs_node* dn = (struct diskfs_node*)kmalloc(sizeof(*dn)); - if (!dn) return -ENOMEM; + if (!dn) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOMEM; } memset(dn, 0, sizeof(*dn)); diskfs_strlcpy(dn->vfs.name, name, sizeof(dn->vfs.name)); dn->vfs.flags = FS_FILE; @@ -904,6 +972,7 @@ static int diskfs_vfs_create(struct fs_node* dir, const char* name, uint32_t fla dn->vfs.i_ops = &diskfs_file_iops; dn->ino = new_ino; *out = &dn->vfs; + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return 0; } @@ -914,12 +983,13 @@ static int diskfs_vfs_mkdir(struct fs_node* dir, const char* name) { struct diskfs_node* parent = (struct diskfs_node*)dir; uint16_t parent_ino = parent->ino; + uintptr_t irq_flags = spin_lock_irqsave(&g_diskfs_lock); struct diskfs_super sb; - if (diskfs_super_load(&sb) < 0) return -EIO; - if (parent_ino >= DISKFS_MAX_INODES) return -EIO; - if (sb.inodes[parent_ino].type != DISKFS_INODE_DIR) return -ENOTDIR; + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } + if (parent_ino >= DISKFS_MAX_INODES) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } + if (sb.inodes[parent_ino].type != DISKFS_INODE_DIR) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOTDIR; } - if (diskfs_find_child(&sb, parent_ino, name) >= 0) return -EEXIST; + if (diskfs_find_child(&sb, parent_ino, name) >= 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EEXIST; } for (uint16_t i = 1; i < DISKFS_MAX_INODES; i++) { if (sb.inodes[i].type != DISKFS_INODE_FREE) continue; @@ -930,9 +1000,12 @@ static int diskfs_vfs_mkdir(struct fs_node* dir, const char* name) { sb.inodes[i].start_lba = 0; sb.inodes[i].size_bytes = 0; sb.inodes[i].cap_sectors = 0; - return diskfs_super_store(&sb); + int ret = diskfs_super_store(&sb); + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); + return ret; } + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOSPC; } @@ -943,19 +1016,22 @@ static int diskfs_vfs_unlink(struct fs_node* dir, const char* name) { struct diskfs_node* parent = (struct diskfs_node*)dir; uint16_t parent_ino = parent->ino; + uintptr_t irq_flags = spin_lock_irqsave(&g_diskfs_lock); struct diskfs_super sb; - if (diskfs_super_load(&sb) < 0) return -EIO; + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } int child = diskfs_find_child(&sb, parent_ino, name); - if (child < 0) return -ENOENT; + if (child < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOENT; } uint16_t ino = (uint16_t)child; - if (ino == 0) return -EPERM; + if (ino == 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EPERM; } - if (sb.inodes[ino].type == DISKFS_INODE_DIR) return -EISDIR; - if (sb.inodes[ino].type != DISKFS_INODE_FILE) return -ENOENT; + if (sb.inodes[ino].type == DISKFS_INODE_DIR) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EISDIR; } + if (sb.inodes[ino].type != DISKFS_INODE_FILE) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOENT; } memset(&sb.inodes[ino], 0, sizeof(sb.inodes[ino])); - return diskfs_super_store(&sb); + int ret = diskfs_super_store(&sb); + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); + return ret; } static int diskfs_vfs_rmdir(struct fs_node* dir, const char* name) { @@ -965,23 +1041,26 @@ static int diskfs_vfs_rmdir(struct fs_node* dir, const char* name) { struct diskfs_node* parent = (struct diskfs_node*)dir; uint16_t parent_ino = parent->ino; + uintptr_t irq_flags = spin_lock_irqsave(&g_diskfs_lock); struct diskfs_super sb; - if (diskfs_super_load(&sb) < 0) return -EIO; + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } int child = diskfs_find_child(&sb, parent_ino, name); - if (child < 0) return -ENOENT; + if (child < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOENT; } uint16_t ino = (uint16_t)child; - if (ino == 0) return -EPERM; - if (sb.inodes[ino].type != DISKFS_INODE_DIR) return -ENOTDIR; + if (ino == 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EPERM; } + if (sb.inodes[ino].type != DISKFS_INODE_DIR) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOTDIR; } /* Check directory is empty */ for (uint16_t i = 0; i < DISKFS_MAX_INODES; i++) { if (sb.inodes[i].type == DISKFS_INODE_FREE) continue; - if (sb.inodes[i].parent == ino && i != ino) return -ENOTEMPTY; + if (sb.inodes[i].parent == ino && i != ino) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOTEMPTY; } } memset(&sb.inodes[ino], 0, sizeof(sb.inodes[ino])); - return diskfs_super_store(&sb); + int ret = diskfs_super_store(&sb); + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); + return ret; } static int diskfs_vfs_rename(struct fs_node* old_dir, const char* old_name, @@ -992,20 +1071,21 @@ static int diskfs_vfs_rename(struct fs_node* old_dir, const char* old_name, struct diskfs_node* odir = (struct diskfs_node*)old_dir; struct diskfs_node* ndir = (struct diskfs_node*)new_dir; + uintptr_t irq_flags = spin_lock_irqsave(&g_diskfs_lock); struct diskfs_super sb; - if (diskfs_super_load(&sb) < 0) return -EIO; + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } int src = diskfs_find_child(&sb, odir->ino, old_name); - if (src < 0) return -ENOENT; + if (src < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOENT; } uint16_t src_ino = (uint16_t)src; - if (src_ino == 0) return -EPERM; + if (src_ino == 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EPERM; } /* Check if destination exists */ int dst = diskfs_find_child(&sb, ndir->ino, new_name); if (dst >= 0) { uint16_t dst_ino = (uint16_t)dst; - if (sb.inodes[dst_ino].type != sb.inodes[src_ino].type) return -EINVAL; - if (dst_ino == src_ino) return 0; + if (sb.inodes[dst_ino].type != sb.inodes[src_ino].type) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EINVAL; } + if (dst_ino == src_ino) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return 0; } memset(&sb.inodes[dst_ino], 0, sizeof(sb.inodes[dst_ino])); } @@ -1013,7 +1093,9 @@ static int diskfs_vfs_rename(struct fs_node* old_dir, const char* old_name, memset(sb.inodes[src_ino].name, 0, sizeof(sb.inodes[src_ino].name)); diskfs_strlcpy(sb.inodes[src_ino].name, new_name, sizeof(sb.inodes[src_ino].name)); - return diskfs_super_store(&sb); + int ret = diskfs_super_store(&sb); + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); + return ret; } static int diskfs_vfs_truncate(struct fs_node* node, uint32_t length) { @@ -1021,16 +1103,20 @@ static int diskfs_vfs_truncate(struct fs_node* node, uint32_t length) { if (!g_ready) return -ENODEV; struct diskfs_node* dn = (struct diskfs_node*)node; + + uintptr_t irq_flags = spin_lock_irqsave(&g_diskfs_lock); struct diskfs_super sb; - if (diskfs_super_load(&sb) < 0) return -EIO; - if (dn->ino >= DISKFS_MAX_INODES) return -EIO; - if (sb.inodes[dn->ino].type != DISKFS_INODE_FILE) return -EISDIR; + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } + if (dn->ino >= DISKFS_MAX_INODES) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } + if (sb.inodes[dn->ino].type != DISKFS_INODE_FILE) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EISDIR; } if (length < sb.inodes[dn->ino].size_bytes) { sb.inodes[dn->ino].size_bytes = length; } node->length = sb.inodes[dn->ino].size_bytes; - return diskfs_super_store(&sb); + int ret = diskfs_super_store(&sb); + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); + return ret; } static int diskfs_vfs_link(struct fs_node* dir, const char* name, struct fs_node* target) { @@ -1040,22 +1126,23 @@ static int diskfs_vfs_link(struct fs_node* dir, const char* name, struct fs_node struct diskfs_node* parent = (struct diskfs_node*)dir; struct diskfs_node* src = (struct diskfs_node*)target; + uintptr_t irq_flags = spin_lock_irqsave(&g_diskfs_lock); struct diskfs_super sb; - if (diskfs_super_load(&sb) < 0) return -EIO; + if (diskfs_super_load(&sb) < 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } uint16_t src_ino = src->ino; - if (src_ino >= DISKFS_MAX_INODES) return -EIO; - if (sb.inodes[src_ino].type != DISKFS_INODE_FILE) return -EPERM; + if (src_ino >= DISKFS_MAX_INODES) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EIO; } + if (sb.inodes[src_ino].type != DISKFS_INODE_FILE) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EPERM; } /* Check new name doesn't already exist */ - if (diskfs_find_child(&sb, parent->ino, name) >= 0) return -EEXIST; + if (diskfs_find_child(&sb, parent->ino, name) >= 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -EEXIST; } /* Find a free inode slot */ uint16_t new_ino = 0; for (uint16_t i = 1; i < DISKFS_MAX_INODES; i++) { if (sb.inodes[i].type == DISKFS_INODE_FREE) { new_ino = i; break; } } - if (new_ino == 0) return -ENOSPC; + if (new_ino == 0) { spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); return -ENOSPC; } /* Create new inode sharing same data blocks */ sb.inodes[new_ino].type = DISKFS_INODE_FILE; @@ -1071,7 +1158,9 @@ static int diskfs_vfs_link(struct fs_node* dir, const char* name, struct fs_node if (sb.inodes[src_ino].nlink < 2) sb.inodes[src_ino].nlink = 2; else sb.inodes[src_ino].nlink++; - return diskfs_super_store(&sb); + int ret = diskfs_super_store(&sb); + spin_unlock_irqrestore(&g_diskfs_lock, irq_flags); + return ret; } fs_node_t* diskfs_create_root(int drive) { -- 2.43.0