added more to writeup-almost done with file 1. i believe this is the longest file
This commit is contained in:
parent
f7c4c13f2a
commit
bc0e7d714b
195
writeup/1.md
195
writeup/1.md
@ -213,4 +213,197 @@ MMP is multi-mount protection: a block holds advanced information for that, and
|
|||||||
u_int8_t e2fs_reserved_pad;
|
u_int8_t e2fs_reserved_pad;
|
||||||
```
|
```
|
||||||
|
|
||||||
The values seem to get more and more disorganized as we reach the end of the block
|
The group seize is for Flexible Block Groups. It is the number of blocks in each group in log powers again, for easy access. The `e2fs_chksum_type` is the algorithm used to determine the checksum of the entire disk. Not quite sure what exact role the `e2fs_encrypt` value holds, but it should have something to do with luks/encrypted files.
|
||||||
|
|
||||||
|
The reserved padding is, well reserved padding. Maybe it's something we have to implement 👀
|
||||||
|
|
||||||
|
```c
|
||||||
|
u_int64_t e2fs_kbytes_written; /* number of lifetime kilobytes */
|
||||||
|
u_int32_t e2fs_snapinum; /* inode number of active snapshot */
|
||||||
|
u_int32_t e2fs_snapid; /* sequential ID of active snapshot */
|
||||||
|
u_int64_t e2fs_snaprbcount; /* rsvd blocks for active snapshot */
|
||||||
|
u_int32_t e2fs_snaplist; /* inode number for on-disk snapshot */
|
||||||
|
u_int32_t e2fs_errcount; /* number of file system errors */
|
||||||
|
u_int32_t e2fs_first_errtime; /* first time an error happened */
|
||||||
|
u_int32_t e2fs_first_errino; /* inode involved in first error */
|
||||||
|
u_int64_t e2fs_first_errblk; /* block involved of first error */
|
||||||
|
u_int8_t e2fs_first_errfunc[32];/* function where error happened */
|
||||||
|
u_int32_t e2fs_first_errline; /* line number where error happened */
|
||||||
|
u_int32_t e2fs_last_errtime; /* most recent time of an error */
|
||||||
|
u_int32_t e2fs_last_errino; /* inode involved in last error */
|
||||||
|
u_int32_t e2fs_last_errline; /* line number where error happened */
|
||||||
|
u_int64_t e2fs_last_errblk; /* block involved of last error */
|
||||||
|
u_int8_t e2fs_last_errfunc[32];/* function where error happened */
|
||||||
|
```
|
||||||
|
|
||||||
|
I'm not quite sure why this would be stored, other than easy access to the journal, but I guess it is useful.
|
||||||
|
|
||||||
|
```c
|
||||||
|
u_int8_t e2fs_mount_opts[64];
|
||||||
|
u_int32_t e2fs_usrquota_inum; /* inode for tracking user quota */
|
||||||
|
u_int32_t e2fs_grpquota_inum; /* inode for tracking group quota */
|
||||||
|
u_int32_t e2fs_overhead_clusters;/* overhead blocks/clusters */
|
||||||
|
u_int32_t e2fs_backup_bgs[2]; /* groups with sparse_super2 SBs */
|
||||||
|
u_int8_t e2fs_encrypt_algos[4];/* encryption algorithms in use */
|
||||||
|
u_int8_t e2fs_encrypt_pw_salt[16];/* salt used for string2key */
|
||||||
|
u_int32_t e2fs_lpf_ino; /* location of the lost+found inode */
|
||||||
|
u_int32_t e2fs_proj_quota_inum; /* inode for tracking project quota */
|
||||||
|
```
|
||||||
|
|
||||||
|
Apart from the overhead clusters, they all make sense. Some inodes hold information; hence the user and groups have certain quotas on how much disk space they can use. The overhead blocks are probably for metadata again; this is a pointer to the block which we had previously looked at with size. Each group has it's own layout table, hence also its own "super-block"; `sparse_super2` must be a standard for the group's super block. Everything else can be looked up online easily, except for the project quota, which is a "set of users". projects(1) should explain it pretty well.
|
||||||
|
|
||||||
|
```c
|
||||||
|
u_int32_t e2fs_chksum_seed; /* checksum seed */
|
||||||
|
u_int32_t e2fs_reserved[98]; /* padding to the end of the block */
|
||||||
|
u_int32_t e2fs_sbchksum; /* superblock checksum */
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
All of which is self-explained. With that, we conclude our thorough analysis of the super block, and we get to the macro declarations which will define the rest of the information on this page.
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* in-memory data for ext2fs */
|
||||||
|
struct m_ext2fs {
|
||||||
|
struct ext2fs e2fs;
|
||||||
|
u_char e2fs_fsmnt[MAXMNTLEN]; /* name mounted on */
|
||||||
|
int8_t e2fs_ronly; /* mounted read-only flag */
|
||||||
|
int8_t e2fs_fmod; /* super block modified flag */
|
||||||
|
int32_t e2fs_fsize; /* fragment size */
|
||||||
|
int32_t e2fs_bsize; /* block size */
|
||||||
|
int32_t e2fs_bshift; /* ``lblkno'' calc of logical blkno */
|
||||||
|
int32_t e2fs_bmask; /* ``blkoff'' calc of blk offsets */
|
||||||
|
int64_t e2fs_qbmask; /* ~fs_bmask - for use with quad size */
|
||||||
|
int32_t e2fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */
|
||||||
|
int32_t e2fs_ncg; /* number of cylinder groups */
|
||||||
|
int32_t e2fs_ngdb; /* number of group descriptor block */
|
||||||
|
int32_t e2fs_ipb; /* number of inodes per block */
|
||||||
|
int32_t e2fs_itpg; /* number of inode table per group */
|
||||||
|
off_t e2fs_maxfilesize; /* depends on LARGE/HUGE flags */
|
||||||
|
struct ext2_gd *e2fs_gd; /* group descriptors */
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Oop, here comes the memory table to crush my dreams 😭
|
||||||
|
Hopefully reading through it explains it, because it appears to be information only the system can use (like, why would you put if the system was mounted read only on the disk 💀) and some redundant values cached again for efficiency.
|
||||||
|
|
||||||
|
```c
|
||||||
|
static inline int
|
||||||
|
e2fs_overflow(struct m_ext2fs *fs, off_t lower, off_t value)
|
||||||
|
{
|
||||||
|
return (value < lower || value > fs->e2fs_maxfilesize);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Here we have a function checking for overflows, but for what? It checks if our current file offset goes beyond the end of the file 🤡 For obvious reasons, this is useful.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define E2FS_MAGIC 0xef53 /* the ext2fs magic number */
|
||||||
|
#define E2FS_REV0 0 /* revision levels */
|
||||||
|
#define E2FS_REV1 1 /* revision levels */
|
||||||
|
```
|
||||||
|
|
||||||
|
Here we have some ways we can identify filesystem: Contrary to prior belief, ext4 is not reivsion 4; there are only two major revisions here, probably between those who made the gap to ext3/4 and those on ext2.
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* compatible/incompatible features */
|
||||||
|
#define EXT2F_COMPAT_PREALLOC 0x0001
|
||||||
|
#define EXT2F_COMPAT_IMAGIC_INODES 0x0002
|
||||||
|
#define EXT2F_COMPAT_HAS_JOURNAL 0x0004
|
||||||
|
#define EXT2F_COMPAT_EXT_ATTR 0x0008
|
||||||
|
#define EXT2F_COMPAT_RESIZE 0x0010
|
||||||
|
#define EXT2F_COMPAT_DIR_INDEX 0x0020
|
||||||
|
#define EXT2F_COMPAT_SPARSE_SUPER2 0x0200
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we are getting to the features we need to implement ourselves and what is already out there for us. They can allocate, they can manage inodes magic numbers, they have a journal (so it is ext3 confirmed), you can resize, read disks, and oh look, you can also use the `sparse_super2` technology when you need to read metadata groups!
|
||||||
|
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define EXT2F_ROCOMPAT_SPARSE_SUPER 0x0001
|
||||||
|
#define EXT2F_ROCOMPAT_LARGE_FILE 0x0002
|
||||||
|
#define EXT2F_ROCOMPAT_BTREE_DIR 0x0004
|
||||||
|
#define EXT2F_ROCOMPAT_HUGE_FILE 0x0008
|
||||||
|
#define EXT2F_ROCOMPAT_GDT_CSUM 0x0010
|
||||||
|
#define EXT2F_ROCOMPAT_DIR_NLINK 0x0020
|
||||||
|
#define EXT2F_ROCOMPAT_EXTRA_ISIZE 0x0040
|
||||||
|
#define EXT2F_ROCOMPAT_QUOTA 0x0100
|
||||||
|
#define EXT2F_ROCOMPAT_BIGALLOC 0x0200
|
||||||
|
#define EXT2F_ROCOMPAT_METADATA_CKSUM 0x0400
|
||||||
|
#define EXT2F_ROCOMPAT_READONLY 0x1000
|
||||||
|
#define EXT2F_ROCOMPAT_PROJECT 0x2000
|
||||||
|
```
|
||||||
|
|
||||||
|
Here are some of the features we need to implement in write mode. Looks pretty self explanatory.
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define EXT2F_INCOMPAT_COMP 0x0001
|
||||||
|
#define EXT2F_INCOMPAT_FTYPE 0x0002
|
||||||
|
#define EXT2F_INCOMPAT_RECOVER 0x0004
|
||||||
|
#define EXT2F_INCOMPAT_JOURNAL_DEV 0x0008
|
||||||
|
#define EXT2F_INCOMPAT_META_BG 0x0010
|
||||||
|
#define EXT2F_INCOMPAT_EXTENTS 0x0040
|
||||||
|
#define EXT2F_INCOMPAT_64BIT 0x0080
|
||||||
|
#define EXT2F_INCOMPAT_MMP 0x0100
|
||||||
|
#define EXT2F_INCOMPAT_FLEX_BG 0x0200
|
||||||
|
#define EXT2F_INCOMPAT_EA_INODE 0x0400
|
||||||
|
#define EXT2F_INCOMPAT_DIRDATA 0x1000
|
||||||
|
#define EXT2F_INCOMPAT_CSUM_SEED 0x2000
|
||||||
|
#define EXT2F_INCOMPAT_LARGEDIR 0x4000
|
||||||
|
#define EXT2F_INCOMPAT_INLINE_DATA 0x8000
|
||||||
|
#define EXT2F_INCOMPAT_ENCRYPT 0x10000
|
||||||
|
```
|
||||||
|
|
||||||
|
And here are the features we need to implement from scratch!
|
||||||
|
|
||||||
|
On attempting to mount an ext4 filesystem read-only on OpenBSD, we get that we don't support `EXT2F_INCOMPAT_64BIT` and `EXT2F_INCOMPAT_CSUM_SEED`. So for our "Minimum Viable Product" we are to focus on that first. 🤓
|
||||||
|
|
||||||
|
Between lines 277 and 313, we have an array of a struct identifying each feature mask to it's string form. That's completely redundant for us; let's skip over it.
|
||||||
|
|
||||||
|
```c
|
||||||
|
/* features supported in this implementation */
|
||||||
|
#define EXT2F_COMPAT_SUPP 0x0000
|
||||||
|
#define EXT2F_ROCOMPAT_SUPP (EXT2F_ROCOMPAT_SPARSE_SUPER | \
|
||||||
|
EXT2F_ROCOMPAT_LARGE_FILE)
|
||||||
|
#define EXT2F_INCOMPAT_SUPP (EXT2F_INCOMPAT_FTYPE)
|
||||||
|
#define EXT4F_RO_INCOMPAT_SUPP (EXT2F_INCOMPAT_EXTENTS | \
|
||||||
|
EXT2F_INCOMPAT_FLEX_BG | \
|
||||||
|
EXT2F_INCOMPAT_META_BG | \
|
||||||
|
EXT2F_INCOMPAT_RECOVER)
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Looks like not all of the features that were supported above are supported? It's hard to say. That might have been some sort of roadmap internal OpenBSD developers wanted to follow. Anyhow, considering that OpenBSD was pretty descriptive with what features we need to implement, we can get to read-only mode soon, and we can have plenty of kernel panics and filesystem corruptions to get to the point we need to get to!
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define E2FS_BEH_CONTINUE 1 /* continue operation */
|
||||||
|
#define E2FS_BEH_READONLY 2 /* remount fs read only */
|
||||||
|
#define E2FS_BEH_PANIC 3 /* cause panic */
|
||||||
|
#define E2FS_BEH_DEFAULT E2FS_BEH_CONTINUE
|
||||||
|
```
|
||||||
|
|
||||||
|
The behavior on errors, now redefined.
|
||||||
|
|
||||||
|
```c
|
||||||
|
/*
|
||||||
|
* OS identification
|
||||||
|
*/
|
||||||
|
#define E2FS_OS_LINUX 0
|
||||||
|
#define E2FS_OS_HURD 1
|
||||||
|
#define E2FS_OS_MASIX 2
|
||||||
|
```
|
||||||
|
|
||||||
|
Hurd as in, GNU Hurd. 🤮
|
||||||
|
No one is using that anymore. I'm pretty sure all operating systems just use the Linux flag at this point.
|
||||||
|
|
||||||
|
```c
|
||||||
|
/*
|
||||||
|
* Filesystem clean flags
|
||||||
|
*/
|
||||||
|
#define E2FS_ISCLEAN 0x01
|
||||||
|
#define E2FS_ERRORS 0x02
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the "dirty bit" you'll sometimes hear `fsck` talk about.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user