1/* @title: Ext2 */
2#include <block/bcache.h>
3#include <block/block.h>
4#include <compiler/core.h>
5#include <err.h>
6#include <fs/vfs.h>
7#include <math/align.h>
8#include <math/min_max.h>
9#include <math/units.h>
10#include <mem/alloc.h>
11#include <stdint.h>
12#include <sync/spinlock.h>
13#include <types/types.h>
14
15/* TODO: move this flaming disaster into internal.h, and also, this file
16 * is going to all get modified BIG TIME when we get actual page cache stuff.
17 *
18 * If you are reading this for the sake of education, DON'T! */
19extern uint64_t PTRS_PER_BLOCK;
20
21#define EXT2_PRIO_DIRENT BIO_RQ_MEDIUM
22#define EXT2_PRIO_INODE BIO_RQ_MEDIUM
23#define EXT2_PRIO_DATA BIO_RQ_HIGH
24#define EXT2_PRIO_BITMAPS BIO_RQ_BACKGROUND
25#define EXT2_PRIO_SBLOCK BIO_RQ_LOW
26
27#define EXT2_NBLOCKS 15
28#define EXT2_SUPERBLOCK_OFFSET 1024
29#define EXT2_SIGNATURE_OFFSET 0x38
30#define EXT2_SIGNATURE 0xEF53
31#define EXT2_NAME_LEN 255
32#define EXT2_ROOT_INODE 2
33#define EXT2_S_IFSOCK 0xC000 // socket
34#define EXT2_S_IFLNK 0xA000 // symbolic link
35#define EXT2_S_IFREG 0x8000 // regular file
36#define EXT2_S_IFBLK 0x6000 // block device
37#define EXT2_S_IFDIR 0x4000 // directory
38#define EXT2_S_IFCHR 0x2000 // character device
39#define EXT2_S_IFIFO 0x1000 // FIFO
40#define EXT2_S_IFMT 0xF000 // mask to extract file type from i_mode
41#define EXT2_S_PERMS 0x0FFF // lower 12 bits: special + rwx bits
42#define EXT2_S_IRWXU 0x01C0 // owner permissions - ALL
43#define EXT2_S_IRWXG 0x0038 // group permissions - ALL
44#define EXT2_S_IRWXO 0x0007 // others permissions - ALL
45#define EXT2_S_PERMS_ONLY 0x01FF // rwx bits only
46
47#define EXT2_S_IRUSR 0x0100 // Owner can read
48#define EXT2_S_IWUSR 0x0080 // Owner can write
49#define EXT2_S_IXUSR 0x0040 // Owner can execute
50#define EXT2_S_IRGRP 0x0020 // Group can read
51#define EXT2_S_IWGRP 0x0010 // Group can write
52#define EXT2_S_IXGRP 0x0008 // Group can execute
53#define EXT2_S_IROTH 0x0004 // Others can read
54#define EXT2_S_IWOTH 0x0002 // Others can write
55#define EXT2_S_IXOTH 0x0001 // Others can execute
56
57#define EXT2_FT_UNKNOWN 0 // Unknown file type
58#define EXT2_FT_REG_FILE 1 // Regular file
59#define EXT2_FT_DIR 2 // Directory
60#define EXT2_FT_CHRDEV 3 // Character device
61#define EXT2_FT_BLKDEV 4 // Block device
62#define EXT2_FT_FIFO 5 // FIFO (named pipe)
63#define EXT2_FT_SOCK 6 // Unix domain socket
64#define EXT2_FT_SYMLINK 7 // Symbolic link
65#define EXT2_FT_MAX 8 // Number of defined file types
66
67// ext2 inode flags
68#define EXT2_SECRM_FL 0x00000001 // Secure deletion
69#define EXT2_UNRM_FL 0x00000002 // Undelete
70#define EXT2_COMPR_FL 0x00000004 // Compress file
71#define EXT2_SYNC_FL 0x00000008 // Synchronous updates
72#define EXT2_IMMUTABLE_FL 0x00000010 // Immutable file
73#define EXT2_APPEND_FL 0x00000020 // Writes only append
74#define EXT2_NODUMP_FL 0x00000040 // Don't include in backups
75#define EXT2_NOATIME_FL 0x00000080 // Don't update access time
76
77#define EXT2_DIRTY_FL 0x00000100 // Dirty (compress support)
78#define EXT2_COMPRBLK_FL 0x00000200 // One or more compressed clusters
79#define EXT2_NOCOMPR_FL 0x00000400 // Don't compress
80#define EXT2_ECOMPR_FL 0x00000800 // Compression error
81#define EXT2_IMAGIC_FL 0x00002000 // AFS directory
82#define EXT2_JOURNAL_DATA_FL 0x00004000 // Write data to journal (data=journal)
83
84#define EXT2_NOTAIL_FL 0x00008000 // File tail not merged (reiserfs)
85#define EXT2_DIRSYNC_FL 0x00010000 // Directory sync updates
86#define EXT2_TOPDIR_FL 0x00020000 // Top of directory hierarchy
87#define EXT2_HUGE_FILE_FL 0x00040000 // Set to each huge file
88#define EXT2_EXTENTS_FL 0x00080000 // Inode uses extents
89
90#define EXT2_EA_INODE_FL 0x00200000 // Inode stores extended attributes
91#define EXT2_INLINE_DATA_FL 0x10000000 // Data is stored inline in inode
92#define EXT2_PROJINHERIT_FL 0x20000000 // Project ID inheritance
93
94#define EXT2_RESERVED_FL 0x80000000 // Reserved for ext3/4
95
96// Useful masks
97#define EXT2_FL_USER_VISIBLE 0x000BDFFF // User-visible flags
98#define EXT2_FL_USER_MODIFIABLE \
99 (EXT2_FL_USER_VISIBLE & ~(EXT2_SECRM_FL | EXT2_UNRM_FL))
100
101#define MAKE_NOP_CALLBACK \
102 static cc_unused bool nop_callback( \
103 struct ext2_fs *fs, struct ext2_dir_entry *entry, void *ctx_ptr, \
104 uint32_t block_num, uint32_t entry_num, uint32_t entry_offset) { \
105 cc_ignore(fs, entry_offset, entry, ctx_ptr, block_num, entry_num); \
106 return false; \
107 }
108
109struct ext2_sblock {
110 uint32_t inodes_count;
111 uint32_t blocks_count;
112 uint32_t r_blocks_count;
113 uint32_t free_blocks_count;
114 uint32_t free_inodes_count;
115 uint32_t first_data_block;
116 uint32_t log_block_size;
117 uint32_t log_frag_size;
118 uint32_t blocks_per_group;
119 uint32_t frags_per_group;
120 uint32_t inodes_per_group;
121 uint32_t mtime;
122 uint32_t wtime;
123 uint16_t mnt_count;
124 uint16_t max_mnt_count;
125 uint16_t magic;
126 uint16_t state;
127 uint16_t errors;
128 uint16_t minor_rev_level;
129 uint32_t lastcheck;
130 uint32_t checkinterval;
131 uint32_t creator_os;
132 uint32_t rev_level;
133 uint16_t def_resuid;
134 uint16_t def_resgid;
135 uint32_t first_ino;
136 uint16_t inode_size;
137 uint16_t block_group_nr;
138 uint32_t feature_compat;
139 uint32_t feature_incompat;
140 uint32_t feature_ro_compat;
141 uint8_t uuid[16];
142 char volume_name[16];
143 char last_mounted[64];
144 uint32_t algorithm_usage_bitmap;
145 uint8_t prealloc_blocks;
146 uint8_t prealloc_dir_blocks;
147 uint16_t padding;
148
149 union {
150 struct {
151 uint32_t journal_uuid[4];
152 uint32_t journal_inum;
153 uint32_t journal_dev;
154 uint32_t last_orphan;
155 uint32_t hash_seed[4];
156 uint8_t def_hash_version;
157 uint8_t journal_backup_type;
158 uint16_t desc_size;
159 uint32_t default_mount_opts;
160 uint32_t first_meta_bg;
161 uint32_t mkfs_time;
162 uint32_t journal_blocks[17];
163 uint32_t quota_group_inode; // [4] → offset 0x258
164 uint32_t quota_project_inode; // [5] → offset 0x25C
165 };
166 uint32_t reserved[204];
167 };
168} cc_packed;
169
170struct ext2_group_desc {
171 uint32_t block_bitmap;
172 uint32_t inode_bitmap;
173 uint32_t inode_table;
174 uint16_t free_blocks_count;
175 uint16_t free_inodes_count;
176 uint16_t used_dirs_count;
177 uint16_t pad;
178 uint32_t reserved[3];
179} cc_packed;
180
181struct ext2_inode {
182 mode_t mode;
183 uint16_t uid;
184 uint32_t size;
185 uint32_t atime;
186 uint32_t ctime;
187 uint32_t mtime;
188 uint32_t dtime;
189 uint16_t gid;
190 uint16_t links_count;
191 uint32_t blocks;
192 uint32_t flags;
193 uint32_t osd1;
194
195 uint32_t block[EXT2_NBLOCKS];
196 uint32_t generation;
197 uint32_t file_acl;
198 uint32_t dir_acl;
199 uint32_t faddr;
200 uint8_t frag[16];
201 uint8_t osd2[12];
202};
203
204struct ext2_full_inode {
205 struct ext2_inode node;
206 inode_t inode_num;
207 struct bcache_entry *ent;
208};
209
210struct ext2_dir_entry {
211 inode_t inode;
212 uint16_t rec_len;
213 uint8_t name_len;
214 uint8_t file_type;
215 char name[EXT2_NAME_LEN + 1];
216} cc_packed;
217
218struct ext2_fs {
219 struct partition *partition;
220 struct block_device *drive;
221 struct ext2_sblock *sblock;
222 struct ext2_group_desc *group_desc;
223 struct bcache_entry *sbcache_ent;
224 struct bcache_entry *gdesc_cache_ent;
225 uint32_t num_groups;
226 uint32_t inodes_count;
227 uint32_t inodes_per_group;
228 uint32_t blocks_per_group;
229 uint32_t block_size;
230 uint32_t sectors_per_block;
231 uint16_t inode_size;
232
233 /* lock the fs struct */
234 struct spinlock lock;
235};
236
237ERR_FACILITY_EXTERN(ext2);
238
239enum ext2_errno {
240 EXT2_ERR_FS_INVALID = ERR_DELTA_START,
241 EXT2_ERR_NO_INODE,
242
243};
244
245typedef bool (*dir_entry_callback)(struct ext2_fs *fs,
246 struct ext2_dir_entry *entry, void *ctx,
247 uint32_t block_num, uint32_t entry_num,
248 uint32_t entry_offset);
249
250typedef void (*ext2_block_visitor)(struct ext2_fs *fs, struct ext2_inode *inode,
251 uint32_t depth, uint32_t *block_ptr,
252 void *user_data);
253
254//
255//
256// R/W + Mount
257//
258//
259
260uint8_t *ext2_block_read(struct ext2_fs *fs, uint32_t block_num,
261 struct bcache_entry **out);
262
263bool ext2_block_write(struct ext2_fs *fs, struct bcache_entry *ent,
264 enum bio_request_priority prio);
265
266bool ext2_read_superblock(struct partition *, struct ext2_sblock *sblock);
267
268bool ext2_write_superblock(struct ext2_fs *fs);
269bool ext2_write_group_desc(struct ext2_fs *fs);
270
271enum err ext2_mount(struct partition *, struct ext2_fs *fs,
272 struct ext2_sblock *sblock, struct vfs_node *out_node);
273
274struct vfs_node *ext2_g_mount(struct partition *);
275
276struct ext2_inode *ext2_inode_read(struct ext2_fs *fs, inode_t inode_idx,
277 struct bcache_entry **out_ent);
278
279bool ext2_inode_write(struct ext2_fs *fs, inode_t inode_num,
280 const struct ext2_inode *inode);
281
282uint32_t ext2_get_or_set_block(struct ext2_fs *fs, struct ext2_inode *inode,
283 uint32_t block_index, uint32_t new_block_num,
284 bool allocate, bool *was_allocated);
285
286//
287//
288// Util
289//
290//
291
292uint32_t ext2_block_to_lba(struct ext2_fs *fs, uint32_t block_num);
293bool ext2_dirent_valid(struct ext2_dir_entry *entry);
294void ext2_init_inode(struct ext2_inode *new_inode, mode_t mode);
295
296void ext2_init_dirent(struct ext2_fs *fs, struct ext2_dir_entry *new_entry,
297 inode_t inode_num, const char *name, uint8_t type);
298
299uint8_t ext2_extract_ftype(mode_t mode);
300
301bool ext2_walk_dir(struct ext2_fs *fs, struct ext2_full_inode *dir,
302 dir_entry_callback cb, void *ctx);
303
304static inline void ext2_dealloc_inode(struct ext2_full_inode *ino) {
305 vfree(ino);
306}
307
308static inline uint32_t ext2_get_block_group(struct ext2_fs *fs,
309 uint32_t block) {
310 return (block - 1) / fs->sblock->blocks_per_group;
311}
312
313static inline uint32_t ext2_get_inode_group(struct ext2_fs *fs, inode_t inode) {
314 return (inode - 1) / fs->inodes_per_group;
315}
316
317static inline enum irql ext2_fs_lock(struct ext2_fs *fs)
318 TSA_ACQUIRES_SPIN(&fs->lock) {
319 return spin_lock(&fs->lock);
320}
321
322static inline void ext2_fs_unlock(struct ext2_fs *fs, enum irql i)
323 TSA_RELEASES_SPIN(&fs->lock) {
324 spin_unlock(&fs->lock, i);
325}
326
327static inline void ext2_prefetch_block(struct ext2_fs *fs, uint32_t block) {
328 uint32_t lba = ext2_block_to_lba(fs, block_num: block);
329 bcache_prefetch_async(disk: fs->drive, lba, block_size: fs->block_size,
330 spb: fs->sectors_per_block);
331}
332
333static inline void ext2_inode_lock(struct ext2_full_inode *ino)
334 TSA_ACQUIRES(&ino->ent->lock) {
335 bcache_ent_lock(ent: ino->ent);
336}
337
338static inline void ext2_inode_unlock(struct ext2_full_inode *ino)
339 TSA_RELEASES(&ino->ent->lock) {
340 bcache_ent_unlock(ent: ino->ent);
341}
342
343static inline uint8_t *ext2_create_bcache_ent(struct ext2_fs *fs,
344 uint32_t block,
345 struct bcache_entry **out) {
346 return bcache_create_ent(disk: fs->drive, lba: ext2_block_to_lba(fs, block_num: block),
347 block_size: fs->block_size, sectors_per_block: fs->sectors_per_block, false, out_entry: out);
348}
349
350//
351//
352// Higher level stuff
353//
354//
355
356enum err ext2_link_file(struct ext2_fs *fs, struct ext2_full_inode *dir_inode,
357 struct ext2_full_inode *inode, const char *name,
358 uint8_t type, bool increment_links);
359
360enum err ext2_unlink_file(struct ext2_fs *fs, struct ext2_full_inode *dir_inode,
361 const char *name, bool free_blocks,
362 bool decrement_links);
363
364enum err ext2_create_file(struct ext2_fs *fs,
365 struct ext2_full_inode *parent_dir, const char *name,
366 mode_t mode, bool increment_links);
367
368enum err ext2_symlink_file(struct ext2_fs *fs,
369 struct ext2_full_inode *dir_inode, const char *name,
370 const char *target);
371
372enum err ext2_write_file(struct ext2_fs *fs, struct ext2_full_inode *inode,
373 uint32_t offset, const uint8_t *src, uint32_t size);
374
375enum err ext2_read_file(struct ext2_fs *fs, struct ext2_full_inode *inode,
376 uint32_t offset, uint8_t *buffer, uint64_t length);
377
378enum err ext2_truncate_file(struct ext2_fs *fs, struct ext2_full_inode *inode,
379 uint32_t new_size);
380
381enum err ext2_chmod(struct ext2_fs *fs, struct ext2_full_inode *node,
382 mode_t new_mode);
383
384enum err ext2_chown(struct ext2_fs *fs, struct ext2_full_inode *node,
385 uid_t new_uid, gid_t new_gid);
386
387enum err ext2_readlink(struct ext2_fs *fs, struct ext2_full_inode *node,
388 char *buf, uint64_t size);
389
390struct ext2_full_inode *ext2_find_file_in_dir(struct ext2_fs *fs,
391 struct ext2_full_inode *dir_inode,
392 const char *fname,
393 uint8_t *type_out);
394
395bool ext2_dir_contains_file(struct ext2_fs *fs,
396 struct ext2_full_inode *dir_inode,
397 const char *fname);
398
399enum err ext2_mkdir(struct ext2_fs *fs, struct ext2_full_inode *parent_dir,
400 const char *name, mode_t mode);
401
402enum err ext2_rmdir(struct ext2_fs *fs, struct ext2_full_inode *parent_dir,
403 const char *name);
404
405enum err ext2_readdir(struct ext2_fs *fs, struct ext2_full_inode *dir_inode,
406 struct ext2_dir_entry *out, uint32_t entry_offset);
407
408//
409//
410//
411// Alloc/dealloc
412//
413//
414
415uint32_t ext2_alloc_block(struct ext2_fs *fs);
416bool ext2_free_block(struct ext2_fs *fs, uint32_t block_num);
417inode_t ext2_alloc_inode(struct ext2_fs *fs);
418bool ext2_free_inode(struct ext2_fs *fs, inode_t inode_num);
419
420bool ext2_find_first_available(struct ext2_fs *fs, struct ext2_full_inode *dir,
421 uint32_t *new_block);
422
423void ext2_traverse_inode_blocks(struct ext2_fs *fs, struct ext2_inode *inode,
424 ext2_block_visitor visitor, void *user_data,
425 bool readahead);
426
427void ext2_g_print(struct partition *);
428
429void ext2_dump_file_data(struct ext2_fs *fs, const struct ext2_inode *inode,
430 uint32_t start_block_index, uint32_t length);
431
432#pragma once
433