mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 21:42:43 +00:00 
			
		
		
		
	Add support for removing directories.
It's really only supported in Ext2FS since SynthFS doesn't really want you mucking around with its files. This is pretty neat though :^) I ran into some trouble with HashMap while working on this but opted to work around it and leave that for a separate investigation.
This commit is contained in:
		
							parent
							
								
									031c62a21e
								
							
						
					
					
						commit
						c95228b128
					
				
					 19 changed files with 185 additions and 40 deletions
				
			
		|  | @ -313,6 +313,20 @@ void Ext2FS::free_inode(Ext2FSInode& inode) | ||||||
|         set_block_allocation_state(group_index, block_index, false); |         set_block_allocation_state(group_index, block_index, false); | ||||||
| 
 | 
 | ||||||
|     set_inode_allocation_state(inode.index(), false); |     set_inode_allocation_state(inode.index(), false); | ||||||
|  | 
 | ||||||
|  |     if (inode.is_directory()) { | ||||||
|  |         auto& bgd = const_cast<ext2_group_desc&>(group_descriptor(group_index_from_inode(inode.index()))); | ||||||
|  |         --bgd.bg_used_dirs_count; | ||||||
|  |         dbgprintf("Ext2FS: decremented bg_used_dirs_count %u -> %u\n", bgd.bg_used_dirs_count - 1, bgd.bg_used_dirs_count); | ||||||
|  |         flush_block_group_descriptor_table(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void Ext2FS::flush_block_group_descriptor_table() | ||||||
|  | { | ||||||
|  |     unsigned blocks_to_write = ceilDiv(m_blockGroupCount * (unsigned)sizeof(ext2_group_desc), blockSize()); | ||||||
|  |     unsigned first_block_of_bgdt = blockSize() == 1024 ? 2 : 1; | ||||||
|  |     writeBlocks(first_block_of_bgdt, blocks_to_write, m_cached_group_descriptor_table); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Ext2FSInode::Ext2FSInode(Ext2FS& fs, unsigned index, const ext2_inode& raw_inode) | Ext2FSInode::Ext2FSInode(Ext2FS& fs, unsigned index, const ext2_inode& raw_inode) | ||||||
|  | @ -356,10 +370,16 @@ void Ext2FSInode::flush_metadata() | ||||||
|     dbgprintf("Ext2FSInode: flush_metadata for inode %u\n", index()); |     dbgprintf("Ext2FSInode: flush_metadata for inode %u\n", index()); | ||||||
|     fs().write_ext2_inode(index(), m_raw_inode); |     fs().write_ext2_inode(index(), m_raw_inode); | ||||||
|     if (is_directory()) { |     if (is_directory()) { | ||||||
|         // FIXME: This invalidation is way too hardcore.
 |         // Unless we're about to go away permanently, invalidate the lookup cache.
 | ||||||
|  |         if (m_raw_inode.i_links_count != 0) { | ||||||
|             LOCKER(m_lock); |             LOCKER(m_lock); | ||||||
|  |             // FIXME: Something isn't working right when we hit this code path.
 | ||||||
|  |             //        I've seen crashes inside HashMap::clear() all the way down in DoublyLinkedList::clear().
 | ||||||
|  |             //        My guess would be a HashTable bug.
 | ||||||
|  |             // FIXME: This invalidation is way too hardcore. It's sad to throw away the whole cache.
 | ||||||
|             m_lookup_cache.clear(); |             m_lookup_cache.clear(); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|     set_metadata_dirty(false); |     set_metadata_dirty(false); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -401,7 +421,7 @@ RetainPtr<Inode> Ext2FS::get_inode(InodeIdentifier inode) const | ||||||
|     return new_inode; |     return new_inode; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ssize_t Ext2FSInode::read_bytes(off_t offset, size_t count, byte* buffer, FileDescriptor*) | ssize_t Ext2FSInode::read_bytes(off_t offset, size_t count, byte* buffer, FileDescriptor*) const | ||||||
| { | { | ||||||
|     ASSERT(offset >= 0); |     ASSERT(offset >= 0); | ||||||
|     if (m_raw_inode.i_size == 0) |     if (m_raw_inode.i_size == 0) | ||||||
|  | @ -550,7 +570,7 @@ ssize_t Ext2FSInode::write_bytes(off_t offset, size_t count, const byte* data, F | ||||||
|     return nwritten; |     return nwritten; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool Ext2FSInode::traverse_as_directory(Function<bool(const FS::DirectoryEntry&)> callback) | bool Ext2FSInode::traverse_as_directory(Function<bool(const FS::DirectoryEntry&)> callback) const | ||||||
| { | { | ||||||
|     ASSERT(metadata().isDirectory()); |     ASSERT(metadata().isDirectory()); | ||||||
| 
 | 
 | ||||||
|  | @ -610,6 +630,9 @@ bool Ext2FSInode::add_child(InodeIdentifier child_id, const String& name, byte f | ||||||
| 
 | 
 | ||||||
| bool Ext2FSInode::remove_child(const String& name, int& error) | bool Ext2FSInode::remove_child(const String& name, int& error) | ||||||
| { | { | ||||||
|  | #ifdef EXT2_DEBUG | ||||||
|  |     dbgprintf("Ext2FSInode::remove_child(%s) in inode %u\n", name.characters(), index()); | ||||||
|  | #endif | ||||||
|     ASSERT(is_directory()); |     ASSERT(is_directory()); | ||||||
| 
 | 
 | ||||||
|     unsigned child_inode_index; |     unsigned child_inode_index; | ||||||
|  | @ -710,7 +733,9 @@ bool Ext2FS::write_directory_inode(unsigned directoryInode, Vector<DirectoryEntr | ||||||
|     kprintf("\n"); |     kprintf("\n"); | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|     return get_inode({ fsid(), directoryInode })->write_bytes(0, directoryData.size(), directoryData.pointer(), nullptr); |     auto directory_inode = get_inode({ fsid(), directoryInode }); | ||||||
|  |     ssize_t nwritten = directory_inode->write_bytes(0, directoryData.size(), directoryData.pointer(), nullptr); | ||||||
|  |     return nwritten == directoryData.size(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| unsigned Ext2FS::inodes_per_block() const | unsigned Ext2FS::inodes_per_block() const | ||||||
|  | @ -955,10 +980,7 @@ bool Ext2FS::set_inode_allocation_state(unsigned index, bool newState) | ||||||
|         ++mutableBGD.bg_free_inodes_count; |         ++mutableBGD.bg_free_inodes_count; | ||||||
|     dbgprintf("Ext2FS: group free inode count %u -> %u\n", bgd.bg_free_inodes_count, bgd.bg_free_inodes_count - 1); |     dbgprintf("Ext2FS: group free inode count %u -> %u\n", bgd.bg_free_inodes_count, bgd.bg_free_inodes_count - 1); | ||||||
| 
 | 
 | ||||||
|     unsigned blocksToWrite = ceilDiv(m_blockGroupCount * (unsigned)sizeof(ext2_group_desc), blockSize()); |     flush_block_group_descriptor_table(); | ||||||
|     unsigned firstBlockOfBGDT = blockSize() == 1024 ? 2 : 1; |  | ||||||
|     writeBlocks(firstBlockOfBGDT, blocksToWrite, m_cached_group_descriptor_table); |  | ||||||
|      |  | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1000,10 +1022,7 @@ bool Ext2FS::set_block_allocation_state(GroupIndex group, BlockIndex bi, bool ne | ||||||
|         ++mutableBGD.bg_free_blocks_count; |         ++mutableBGD.bg_free_blocks_count; | ||||||
|     dbgprintf("Ext2FS: group free block count %u -> %u\n", bgd.bg_free_blocks_count, bgd.bg_free_blocks_count - 1); |     dbgprintf("Ext2FS: group free block count %u -> %u\n", bgd.bg_free_blocks_count, bgd.bg_free_blocks_count - 1); | ||||||
| 
 | 
 | ||||||
|     unsigned blocksToWrite = ceilDiv(m_blockGroupCount * (unsigned)sizeof(ext2_group_desc), blockSize()); |     flush_block_group_descriptor_table(); | ||||||
|     unsigned firstBlockOfBGDT = blockSize() == 1024 ? 2 : 1; |  | ||||||
|     writeBlocks(firstBlockOfBGDT, blocksToWrite, m_cached_group_descriptor_table); |  | ||||||
|      |  | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -1040,9 +1059,7 @@ RetainPtr<Inode> Ext2FS::create_directory(InodeIdentifier parent_id, const Strin | ||||||
|     ++bgd.bg_used_dirs_count; |     ++bgd.bg_used_dirs_count; | ||||||
|     dbgprintf("Ext2FS: incremented bg_used_dirs_count %u -> %u\n", bgd.bg_used_dirs_count - 1, bgd.bg_used_dirs_count); |     dbgprintf("Ext2FS: incremented bg_used_dirs_count %u -> %u\n", bgd.bg_used_dirs_count - 1, bgd.bg_used_dirs_count); | ||||||
| 
 | 
 | ||||||
|     unsigned blocksToWrite = ceilDiv(m_blockGroupCount * (unsigned)sizeof(ext2_group_desc), blockSize()); |     flush_block_group_descriptor_table(); | ||||||
|     unsigned firstBlockOfBGDT = blockSize() == 1024 ? 2 : 1; |  | ||||||
|     writeBlocks(firstBlockOfBGDT, blocksToWrite, m_cached_group_descriptor_table); |  | ||||||
| 
 | 
 | ||||||
|     error = 0; |     error = 0; | ||||||
|     return inode; |     return inode; | ||||||
|  | @ -1165,7 +1182,7 @@ RetainPtr<Inode> Ext2FSInode::parent() const | ||||||
|     return fs().get_inode(m_parent_id); |     return fs().get_inode(m_parent_id); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void Ext2FSInode::populate_lookup_cache() | void Ext2FSInode::populate_lookup_cache() const | ||||||
| { | { | ||||||
|     { |     { | ||||||
|         LOCKER(m_lock); |         LOCKER(m_lock); | ||||||
|  | @ -1267,3 +1284,11 @@ void Ext2FS::uncache_inode(InodeIndex index) | ||||||
|     LOCKER(m_inode_cache_lock); |     LOCKER(m_inode_cache_lock); | ||||||
|     m_inode_cache.remove(index); |     m_inode_cache.remove(index); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | size_t Ext2FSInode::directory_entry_count() const | ||||||
|  | { | ||||||
|  |     ASSERT(is_directory()); | ||||||
|  |     populate_lookup_cache(); | ||||||
|  |     LOCKER(m_lock); | ||||||
|  |     return m_lookup_cache.size(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -25,9 +25,9 @@ public: | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     // ^Inode
 |     // ^Inode
 | ||||||
|     virtual ssize_t read_bytes(off_t, size_t, byte* buffer, FileDescriptor*) override; |     virtual ssize_t read_bytes(off_t, size_t, byte* buffer, FileDescriptor*) const override; | ||||||
|     virtual InodeMetadata metadata() const override; |     virtual InodeMetadata metadata() const override; | ||||||
|     virtual bool traverse_as_directory(Function<bool(const FS::DirectoryEntry&)>) override; |     virtual bool traverse_as_directory(Function<bool(const FS::DirectoryEntry&)>) const override; | ||||||
|     virtual InodeIdentifier lookup(const String& name) override; |     virtual InodeIdentifier lookup(const String& name) override; | ||||||
|     virtual String reverse_lookup(InodeIdentifier) override; |     virtual String reverse_lookup(InodeIdentifier) override; | ||||||
|     virtual void flush_metadata() override; |     virtual void flush_metadata() override; | ||||||
|  | @ -40,15 +40,16 @@ private: | ||||||
|     virtual int set_mtime(time_t) override; |     virtual int set_mtime(time_t) override; | ||||||
|     virtual int increment_link_count() override; |     virtual int increment_link_count() override; | ||||||
|     virtual int decrement_link_count() override; |     virtual int decrement_link_count() override; | ||||||
|  |     virtual size_t directory_entry_count() const override; | ||||||
| 
 | 
 | ||||||
|     void populate_lookup_cache(); |     void populate_lookup_cache() const; | ||||||
| 
 | 
 | ||||||
|     Ext2FS& fs(); |     Ext2FS& fs(); | ||||||
|     const Ext2FS& fs() const; |     const Ext2FS& fs() const; | ||||||
|     Ext2FSInode(Ext2FS&, unsigned index, const ext2_inode&); |     Ext2FSInode(Ext2FS&, unsigned index, const ext2_inode&); | ||||||
| 
 | 
 | ||||||
|     Vector<unsigned> m_block_list; |     mutable Vector<unsigned> m_block_list; | ||||||
|     HashMap<String, unsigned> m_lookup_cache; |     mutable HashMap<String, unsigned> m_lookup_cache; | ||||||
|     ext2_inode m_raw_inode; |     ext2_inode m_raw_inode; | ||||||
|     mutable InodeIdentifier m_parent_id; |     mutable InodeIdentifier m_parent_id; | ||||||
| }; | }; | ||||||
|  | @ -68,6 +69,7 @@ private: | ||||||
| 
 | 
 | ||||||
|     const ext2_super_block& super_block() const; |     const ext2_super_block& super_block() const; | ||||||
|     const ext2_group_desc& group_descriptor(unsigned groupIndex) const; |     const ext2_group_desc& group_descriptor(unsigned groupIndex) const; | ||||||
|  |     void flush_block_group_descriptor_table(); | ||||||
|     unsigned first_block_of_group(unsigned groupIndex) const; |     unsigned first_block_of_group(unsigned groupIndex) const; | ||||||
|     unsigned inodes_per_block() const; |     unsigned inodes_per_block() const; | ||||||
|     unsigned inodes_per_group() const; |     unsigned inodes_per_group() const; | ||||||
|  |  | ||||||
|  | @ -48,7 +48,7 @@ FS* FS::from_fsid(dword id) | ||||||
|     return nullptr; |     return nullptr; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ByteBuffer Inode::read_entire(FileDescriptor* descriptor) | ByteBuffer Inode::read_entire(FileDescriptor* descriptor) const | ||||||
| { | { | ||||||
|     size_t initial_size = metadata().size ? metadata().size : 4096; |     size_t initial_size = metadata().size ? metadata().size : 4096; | ||||||
|     auto contents = ByteBuffer::create_uninitialized(initial_size); |     auto contents = ByteBuffer::create_uninitialized(initial_size); | ||||||
|  |  | ||||||
|  | @ -78,16 +78,17 @@ public: | ||||||
|     InodeIdentifier identifier() const { return { fsid(), index() }; } |     InodeIdentifier identifier() const { return { fsid(), index() }; } | ||||||
|     virtual InodeMetadata metadata() const = 0; |     virtual InodeMetadata metadata() const = 0; | ||||||
| 
 | 
 | ||||||
|     ByteBuffer read_entire(FileDescriptor* = nullptr); |     ByteBuffer read_entire(FileDescriptor* = nullptr) const; | ||||||
| 
 | 
 | ||||||
|     virtual ssize_t read_bytes(off_t, size_t, byte* buffer, FileDescriptor*) = 0; |     virtual ssize_t read_bytes(off_t, size_t, byte* buffer, FileDescriptor*) const = 0; | ||||||
|     virtual bool traverse_as_directory(Function<bool(const FS::DirectoryEntry&)>) = 0; |     virtual bool traverse_as_directory(Function<bool(const FS::DirectoryEntry&)>) const = 0; | ||||||
|     virtual InodeIdentifier lookup(const String& name) = 0; |     virtual InodeIdentifier lookup(const String& name) = 0; | ||||||
|     virtual String reverse_lookup(InodeIdentifier) = 0; |     virtual String reverse_lookup(InodeIdentifier) = 0; | ||||||
|     virtual ssize_t write_bytes(off_t, size_t, const byte* data, FileDescriptor*) = 0; |     virtual ssize_t write_bytes(off_t, size_t, const byte* data, FileDescriptor*) = 0; | ||||||
|     virtual bool add_child(InodeIdentifier child_id, const String& name, byte file_type, int& error) = 0; |     virtual bool add_child(InodeIdentifier child_id, const String& name, byte file_type, int& error) = 0; | ||||||
|     virtual bool remove_child(const String& name, int& error) = 0; |     virtual bool remove_child(const String& name, int& error) = 0; | ||||||
|     virtual RetainPtr<Inode> parent() const = 0; |     virtual RetainPtr<Inode> parent() const = 0; | ||||||
|  |     virtual size_t directory_entry_count() const = 0; | ||||||
| 
 | 
 | ||||||
|     bool is_metadata_dirty() const { return m_metadata_dirty; } |     bool is_metadata_dirty() const { return m_metadata_dirty; } | ||||||
| 
 | 
 | ||||||
|  | @ -109,7 +110,7 @@ protected: | ||||||
|     Inode(FS& fs, unsigned index); |     Inode(FS& fs, unsigned index); | ||||||
|     void set_metadata_dirty(bool b) { m_metadata_dirty = b; } |     void set_metadata_dirty(bool b) { m_metadata_dirty = b; } | ||||||
| 
 | 
 | ||||||
|     Lock m_lock; |     mutable Lock m_lock; | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     FS& m_fs; |     FS& m_fs; | ||||||
|  |  | ||||||
|  | @ -2101,6 +2101,16 @@ int Process::sys$unlink(const char* pathname) | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | int Process::sys$rmdir(const char* pathname) | ||||||
|  | { | ||||||
|  |     if (!validate_read_str(pathname)) | ||||||
|  |         return -EFAULT; | ||||||
|  |     int error; | ||||||
|  |     if (!VFS::the().rmdir(String(pathname), *cwd_inode(), error)) | ||||||
|  |         return error; | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int Process::sys$read_tsc(dword* lsw, dword* msw) | int Process::sys$read_tsc(dword* lsw, dword* msw) | ||||||
| { | { | ||||||
|     if (!validate_write_typed(lsw)) |     if (!validate_write_typed(lsw)) | ||||||
|  |  | ||||||
|  | @ -194,6 +194,7 @@ public: | ||||||
|     clock_t sys$times(tms*); |     clock_t sys$times(tms*); | ||||||
|     int sys$utime(const char* pathname, const struct utimbuf*); |     int sys$utime(const char* pathname, const struct utimbuf*); | ||||||
|     int sys$unlink(const char* pathname); |     int sys$unlink(const char* pathname); | ||||||
|  |     int sys$rmdir(const char* pathname); | ||||||
|     int sys$read_tsc(dword* lsw, dword* msw); |     int sys$read_tsc(dword* lsw, dword* msw); | ||||||
| 
 | 
 | ||||||
|     int gui$create_window(const GUI_WindowParameters*); |     int gui$create_window(const GUI_WindowParameters*); | ||||||
|  |  | ||||||
|  | @ -200,7 +200,7 @@ InodeMetadata SynthFSInode::metadata() const | ||||||
|     return m_metadata; |     return m_metadata; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ssize_t SynthFSInode::read_bytes(off_t offset, size_t count, byte* buffer, FileDescriptor* descriptor) | ssize_t SynthFSInode::read_bytes(off_t offset, size_t count, byte* buffer, FileDescriptor* descriptor) const | ||||||
| { | { | ||||||
| #ifdef SYNTHFS_DEBUG | #ifdef SYNTHFS_DEBUG | ||||||
|     kprintf("SynthFS: read_bytes %u\n", index()); |     kprintf("SynthFS: read_bytes %u\n", index()); | ||||||
|  | @ -211,10 +211,10 @@ ssize_t SynthFSInode::read_bytes(off_t offset, size_t count, byte* buffer, FileD | ||||||
|     ByteBuffer generatedData; |     ByteBuffer generatedData; | ||||||
|     if (m_generator) { |     if (m_generator) { | ||||||
|         if (!descriptor) { |         if (!descriptor) { | ||||||
|             generatedData = m_generator(*this); |             generatedData = m_generator(const_cast<SynthFSInode&>(*this)); | ||||||
|         } else { |         } else { | ||||||
|             if (!descriptor->generator_cache()) |             if (!descriptor->generator_cache()) | ||||||
|                 descriptor->generator_cache() = m_generator(*this); |                 descriptor->generator_cache() = m_generator(const_cast<SynthFSInode&>(*this)); | ||||||
|             generatedData = descriptor->generator_cache(); |             generatedData = descriptor->generator_cache(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -227,7 +227,7 @@ ssize_t SynthFSInode::read_bytes(off_t offset, size_t count, byte* buffer, FileD | ||||||
|     return nread; |     return nread; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool SynthFSInode::traverse_as_directory(Function<bool(const FS::DirectoryEntry&)> callback) | bool SynthFSInode::traverse_as_directory(Function<bool(const FS::DirectoryEntry&)> callback) const | ||||||
| { | { | ||||||
|     InterruptDisabler disabler; |     InterruptDisabler disabler; | ||||||
| #ifdef SYNTHFS_DEBUG | #ifdef SYNTHFS_DEBUG | ||||||
|  | @ -305,3 +305,10 @@ bool SynthFSInode::remove_child(const String& name, int& error) | ||||||
| SynthFSInodeCustomData::~SynthFSInodeCustomData() | SynthFSInodeCustomData::~SynthFSInodeCustomData() | ||||||
| { | { | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | size_t SynthFSInode::directory_entry_count() const | ||||||
|  | { | ||||||
|  |     ASSERT(is_directory()); | ||||||
|  |     // NOTE: The 2 is for '.' and '..'
 | ||||||
|  |     return m_children.size() + 2; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -54,9 +54,9 @@ public: | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     // ^Inode
 |     // ^Inode
 | ||||||
|     virtual ssize_t read_bytes(off_t, size_t, byte* buffer, FileDescriptor*) override; |     virtual ssize_t read_bytes(off_t, size_t, byte* buffer, FileDescriptor*) const override; | ||||||
|     virtual InodeMetadata metadata() const override; |     virtual InodeMetadata metadata() const override; | ||||||
|     virtual bool traverse_as_directory(Function<bool(const FS::DirectoryEntry&)>) override; |     virtual bool traverse_as_directory(Function<bool(const FS::DirectoryEntry&)>) const override; | ||||||
|     virtual InodeIdentifier lookup(const String& name) override; |     virtual InodeIdentifier lookup(const String& name) override; | ||||||
|     virtual String reverse_lookup(InodeIdentifier) override; |     virtual String reverse_lookup(InodeIdentifier) override; | ||||||
|     virtual void flush_metadata() override; |     virtual void flush_metadata() override; | ||||||
|  | @ -64,6 +64,7 @@ private: | ||||||
|     virtual bool add_child(InodeIdentifier child_id, const String& name, byte file_type, int& error) override; |     virtual bool add_child(InodeIdentifier child_id, const String& name, byte file_type, int& error) override; | ||||||
|     virtual bool remove_child(const String& name, int& error) override; |     virtual bool remove_child(const String& name, int& error) override; | ||||||
|     virtual RetainPtr<Inode> parent() const override; |     virtual RetainPtr<Inode> parent() const override; | ||||||
|  |     virtual size_t directory_entry_count() const override; | ||||||
| 
 | 
 | ||||||
|     SynthFS& fs(); |     SynthFS& fs(); | ||||||
|     const SynthFS& fs() const; |     const SynthFS& fs() const; | ||||||
|  |  | ||||||
|  | @ -219,6 +219,8 @@ static dword handle(RegisterDump& regs, dword function, dword arg1, dword arg2, | ||||||
|         return current->gui$notify_paint_finished((int)arg1, (const GUI_Rect*)arg2); |         return current->gui$notify_paint_finished((int)arg1, (const GUI_Rect*)arg2); | ||||||
|     case Syscall::SC_gui_set_global_cursor_tracking_enabled: |     case Syscall::SC_gui_set_global_cursor_tracking_enabled: | ||||||
|         return current->gui$set_global_cursor_tracking_enabled((int)arg1, (bool)arg2); |         return current->gui$set_global_cursor_tracking_enabled((int)arg1, (bool)arg2); | ||||||
|  |     case Syscall::SC_rmdir: | ||||||
|  |         return current->sys$rmdir((const char*)arg1); | ||||||
|     default: |     default: | ||||||
|         kprintf("<%u> int0x80: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); |         kprintf("<%u> int0x80: Unknown function %u requested {%x, %x, %x}\n", current->pid(), function, arg1, arg2, arg3); | ||||||
|         break; |         break; | ||||||
|  |  | ||||||
|  | @ -82,6 +82,7 @@ | ||||||
|     __ENUMERATE_SYSCALL(gui_set_window_rect) \ |     __ENUMERATE_SYSCALL(gui_set_window_rect) \ | ||||||
|     __ENUMERATE_SYSCALL(gui_notify_paint_finished) \ |     __ENUMERATE_SYSCALL(gui_notify_paint_finished) \ | ||||||
|     __ENUMERATE_SYSCALL(gui_set_global_cursor_tracking_enabled) \ |     __ENUMERATE_SYSCALL(gui_set_global_cursor_tracking_enabled) \ | ||||||
|  |     __ENUMERATE_SYSCALL(rmdir) \ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #ifdef SERENITY | #ifdef SERENITY | ||||||
|  |  | ||||||
|  | @ -260,6 +260,65 @@ bool VFS::unlink(const String& path, Inode& base, int& error) | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | bool VFS::rmdir(const String& path, Inode& base, int& error) | ||||||
|  | { | ||||||
|  |     error = -EWHYTHO; | ||||||
|  |     // FIXME: This won't work nicely across mount boundaries.
 | ||||||
|  |     FileSystemPath p(path); | ||||||
|  |     if (!p.is_valid()) { | ||||||
|  |         error = -EINVAL; | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     InodeIdentifier parent_dir; | ||||||
|  |     auto inode_id = resolve_path(path, base.identifier(), error, 0, &parent_dir); | ||||||
|  |     if (!inode_id.is_valid()) { | ||||||
|  |         error = -ENOENT; | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (inode_id.fs()->is_readonly()) { | ||||||
|  |         error = -EROFS; | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // FIXME: We should return EINVAL if the last component of the path is "."
 | ||||||
|  |     // FIXME: We should return ENOTEMPTY if the last component of the path is ".."
 | ||||||
|  | 
 | ||||||
|  |     auto inode = get_inode(inode_id); | ||||||
|  |     if (!inode->is_directory()) { | ||||||
|  |         error = -ENOTDIR; | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (inode->directory_entry_count() != 2) { | ||||||
|  |         error = -ENOTEMPTY; | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto parent_inode = get_inode(parent_dir); | ||||||
|  |     ASSERT(parent_inode); | ||||||
|  | 
 | ||||||
|  |     dbgprintf("VFS::rmdir: Removing inode %u:%u from parent %u:%u\n", inode_id.fsid(), inode_id.index(), parent_dir.fsid(), parent_dir.index()); | ||||||
|  | 
 | ||||||
|  |     // To do:
 | ||||||
|  |     // - Remove '.' in target (--child.link_count)
 | ||||||
|  |     // - Remove '..' in target (--parent.link_count)
 | ||||||
|  |     // - Remove target from its parent (--parent.link_count)
 | ||||||
|  |     if (!inode->remove_child(".", error)) | ||||||
|  |         return false; | ||||||
|  | 
 | ||||||
|  |     if (!inode->remove_child("..", error)) | ||||||
|  |         return false; | ||||||
|  | 
 | ||||||
|  |     // FIXME: The reverse_lookup here can definitely be avoided.
 | ||||||
|  |     if (!parent_inode->remove_child(parent_inode->reverse_lookup(inode_id), error)) | ||||||
|  |         return false; | ||||||
|  | 
 | ||||||
|  |     error = 0; | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| InodeIdentifier VFS::resolve_symbolic_link(InodeIdentifier base, Inode& symlink_inode, int& error) | InodeIdentifier VFS::resolve_symbolic_link(InodeIdentifier base, Inode& symlink_inode, int& error) | ||||||
| { | { | ||||||
|     auto symlink_contents = symlink_inode.read_entire(); |     auto symlink_contents = symlink_inode.read_entire(); | ||||||
|  | @ -315,7 +374,7 @@ String VFS::absolute_path(Inode& core_inode) | ||||||
|     return builder.build(); |     return builder.build(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| InodeIdentifier VFS::resolve_path(const String& path, InodeIdentifier base, int& error, int options, InodeIdentifier* deepest_dir) | InodeIdentifier VFS::resolve_path(const String& path, InodeIdentifier base, int& error, int options, InodeIdentifier* parent_id) | ||||||
| { | { | ||||||
|     if (path.is_empty()) { |     if (path.is_empty()) { | ||||||
|         error = -EINVAL; |         error = -EINVAL; | ||||||
|  | @ -330,8 +389,8 @@ InodeIdentifier VFS::resolve_path(const String& path, InodeIdentifier base, int& | ||||||
|     else |     else | ||||||
|         crumb_id = base.is_valid() ? base : root_inode_id(); |         crumb_id = base.is_valid() ? base : root_inode_id(); | ||||||
| 
 | 
 | ||||||
|     if (deepest_dir) |     if (parent_id) | ||||||
|         *deepest_dir = crumb_id; |         *parent_id = crumb_id; | ||||||
| 
 | 
 | ||||||
|     for (unsigned i = 0; i < parts.size(); ++i) { |     for (unsigned i = 0; i < parts.size(); ++i) { | ||||||
|         bool inode_was_root_at_head_of_loop = crumb_id.is_root_inode(); |         bool inode_was_root_at_head_of_loop = crumb_id.is_root_inode(); | ||||||
|  | @ -383,8 +442,10 @@ InodeIdentifier VFS::resolve_path(const String& path, InodeIdentifier base, int& | ||||||
|         crumb_inode = get_inode(crumb_id); |         crumb_inode = get_inode(crumb_id); | ||||||
|         metadata = crumb_inode->metadata(); |         metadata = crumb_inode->metadata(); | ||||||
|         if (metadata.isDirectory()) { |         if (metadata.isDirectory()) { | ||||||
|             if (deepest_dir) |             if (i != parts.size() - 1) { | ||||||
|                 *deepest_dir = crumb_id; |                 if (parent_id) | ||||||
|  |                     *parent_id = crumb_id; | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|         if (metadata.isSymbolicLink()) { |         if (metadata.isSymbolicLink()) { | ||||||
|             if (i == parts.size() - 1) { |             if (i == parts.size() - 1) { | ||||||
|  |  | ||||||
|  | @ -68,6 +68,7 @@ public: | ||||||
|     RetainPtr<FileDescriptor> create(const String& path, int& error, int options, mode_t mode, InodeIdentifier base); |     RetainPtr<FileDescriptor> create(const String& path, int& error, int options, mode_t mode, InodeIdentifier base); | ||||||
|     bool mkdir(const String& path, mode_t mode, InodeIdentifier base, int& error); |     bool mkdir(const String& path, mode_t mode, InodeIdentifier base, int& error); | ||||||
|     bool unlink(const String& path, Inode& base, int& error); |     bool unlink(const String& path, Inode& base, int& error); | ||||||
|  |     bool rmdir(const String& path, Inode& base, int& error); | ||||||
| 
 | 
 | ||||||
|     void register_character_device(CharacterDevice&); |     void register_character_device(CharacterDevice&); | ||||||
| 
 | 
 | ||||||
|  | @ -90,7 +91,7 @@ private: | ||||||
|     bool is_vfs_root(InodeIdentifier) const; |     bool is_vfs_root(InodeIdentifier) const; | ||||||
| 
 | 
 | ||||||
|     void traverse_directory_inode(Inode&, Function<bool(const FS::DirectoryEntry&)>); |     void traverse_directory_inode(Inode&, Function<bool(const FS::DirectoryEntry&)>); | ||||||
|     InodeIdentifier resolve_path(const String& path, InodeIdentifier base, int& error, int options = 0, InodeIdentifier* deepest_dir = nullptr); |     InodeIdentifier resolve_path(const String& path, InodeIdentifier base, int& error, int options = 0, InodeIdentifier* parent_id = nullptr); | ||||||
|     InodeIdentifier resolve_symbolic_link(InodeIdentifier base, Inode& symlink_inode, int& error); |     InodeIdentifier resolve_symbolic_link(InodeIdentifier base, Inode& symlink_inode, int& error); | ||||||
| 
 | 
 | ||||||
|     Mount* find_mount_for_host(InodeIdentifier); |     Mount* find_mount_for_host(InodeIdentifier); | ||||||
|  |  | ||||||
|  | @ -43,6 +43,7 @@ cp -v ../Userland/touch mnt/bin/touch | ||||||
| cp -v ../Userland/sync mnt/bin/sync | cp -v ../Userland/sync mnt/bin/sync | ||||||
| cp -v ../Userland/more mnt/bin/more | cp -v ../Userland/more mnt/bin/more | ||||||
| cp -v ../Userland/rm mnt/bin/rm | cp -v ../Userland/rm mnt/bin/rm | ||||||
|  | cp -v ../Userland/rmdir mnt/bin/rmdir | ||||||
| cp -v ../Userland/cp mnt/bin/cp | cp -v ../Userland/cp mnt/bin/cp | ||||||
| cp -v ../Userland/guitest mnt/bin/guitest | cp -v ../Userland/guitest mnt/bin/guitest | ||||||
| cp -v ../Userland/guitest2 mnt/bin/guitest2 | cp -v ../Userland/guitest2 mnt/bin/guitest2 | ||||||
|  |  | ||||||
|  | @ -44,6 +44,7 @@ | ||||||
|     __ERROR(EWHYTHO,        "Failed without setting an error code (Bug!)") \ |     __ERROR(EWHYTHO,        "Failed without setting an error code (Bug!)") \ | ||||||
|     __ERROR(EBADWINDOW,     "Bad window ID") \ |     __ERROR(EBADWINDOW,     "Bad window ID") \ | ||||||
|     __ERROR(EBADBACKING,    "Bad backing store ID") \ |     __ERROR(EBADBACKING,    "Bad backing store ID") \ | ||||||
|  |     __ERROR(ENOTEMPTY,      "Directory not empty") \ | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| enum __errno_values { | enum __errno_values { | ||||||
|  |  | ||||||
|  | @ -210,6 +210,12 @@ int unlink(const char* pathname) | ||||||
|     __RETURN_WITH_ERRNO(rc, rc, -1); |     __RETURN_WITH_ERRNO(rc, rc, -1); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | int rmdir(const char* pathname) | ||||||
|  | { | ||||||
|  |     int rc = syscall(SC_rmdir, pathname); | ||||||
|  |     __RETURN_WITH_ERRNO(rc, rc, -1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int isatty(int fd) | int isatty(int fd) | ||||||
| { | { | ||||||
|     int rc = syscall(SC_isatty, fd); |     int rc = syscall(SC_isatty, fd); | ||||||
|  |  | ||||||
|  | @ -48,6 +48,7 @@ int ttyname_r(int fd, char* buffer, size_t); | ||||||
| off_t lseek(int fd, off_t, int whence); | off_t lseek(int fd, off_t, int whence); | ||||||
| int link(const char* oldpath, const char* newpath); | int link(const char* oldpath, const char* newpath); | ||||||
| int unlink(const char* pathname); | int unlink(const char* pathname); | ||||||
|  | int rmdir(const char* pathname); | ||||||
| int getdtablesize(); | int getdtablesize(); | ||||||
| int dup(int old_fd); | int dup(int old_fd); | ||||||
| int dup2(int old_fd, int new_fd); | int dup2(int old_fd, int new_fd); | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								Userland/.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								Userland/.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -26,3 +26,4 @@ guitest2 | ||||||
| sysctl | sysctl | ||||||
| rm | rm | ||||||
| cp | cp | ||||||
|  | rmdir | ||||||
|  |  | ||||||
|  | @ -23,6 +23,7 @@ OBJS = \ | ||||||
|        guitest2.o \
 |        guitest2.o \
 | ||||||
|        sysctl.o \
 |        sysctl.o \
 | ||||||
|        cp.o \
 |        cp.o \
 | ||||||
|  |        rmdir.o \
 | ||||||
|        rm.o |        rm.o | ||||||
| 
 | 
 | ||||||
| APPS = \
 | APPS = \
 | ||||||
|  | @ -51,6 +52,7 @@ APPS = \ | ||||||
|        guitest2 \
 |        guitest2 \
 | ||||||
|        sysctl \
 |        sysctl \
 | ||||||
|        cp \
 |        cp \
 | ||||||
|  |        rmdir \
 | ||||||
|        rm |        rm | ||||||
| 
 | 
 | ||||||
| ARCH_FLAGS = | ARCH_FLAGS = | ||||||
|  | @ -149,6 +151,9 @@ cp: cp.o | ||||||
| rm: rm.o | rm: rm.o | ||||||
| 	$(LD) -o $@ $(LDFLAGS) $< ../LibC/LibC.a | 	$(LD) -o $@ $(LDFLAGS) $< ../LibC/LibC.a | ||||||
| 
 | 
 | ||||||
|  | rmdir: rmdir.o | ||||||
|  | 	$(LD) -o $@ $(LDFLAGS) $< ../LibC/LibC.a | ||||||
|  | 
 | ||||||
| .cpp.o: | .cpp.o: | ||||||
| 	@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< | 	@echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										18
									
								
								Userland/rmdir.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								Userland/rmdir.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | 
 | ||||||
|  | int main(int argc, char** argv) | ||||||
|  | { | ||||||
|  |     if (argc != 2) { | ||||||
|  |         fprintf(stderr, "usage: rmdir <path>\n"); | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  |     int rc = rmdir(argv[1]); | ||||||
|  |     if (rc < 0) { | ||||||
|  |         perror("rmdir"); | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling