got a bit deeper into ext4
This commit is contained in:
parent
09425c76cf
commit
af7345c6b2
96
writeup/3.md
96
writeup/3.md
|
@ -38,4 +38,100 @@ I guess our question starts at, what exactly is a virtual node? It's nothing mor
|
|||
|
||||
Meaning nothing more than what we have to do in order for there to be no difference when we run `ls`, `cd`, `rm`, `mkdir`, etc. Abstractions are necessary.
|
||||
|
||||
```c
|
||||
if ((error = ext2fs_mountfs(rootvp, mp, p)) != 0) {
|
||||
vfs_unbusy(mp);
|
||||
vfs_mount_free(mp);
|
||||
vrele(rootvp);
|
||||
return (error);
|
||||
}
|
||||
```
|
||||
|
||||
Looks like something that would case a critical error, causing you to get rid of everything else- it's inviting me to take a look! Let's dive into that function as well.
|
||||
|
||||
```c
|
||||
int
|
||||
ext2fs_mountfs(struct vnode *devvp, struct mount *mp, struct proc *p)
|
||||
```
|
||||
|
||||
We have a virtual node (dev virtual pointer? what does that stand for?) a mount point, and proc which is probably something to do with the device driver.
|
||||
|
||||
```c
|
||||
struct ufsmount *ump;
|
||||
struct buf *bp;
|
||||
struct ext2fs *fs;
|
||||
dev_t dev;
|
||||
int error, ronly;
|
||||
struct ucred *cred;
|
||||
|
||||
dev = devvp->v_rdev;
|
||||
cred = p ? p->p_ucred : NOCRED;
|
||||
```
|
||||
|
||||
`*bp` appears to be a buffer pointer for the filesystem
|
||||
Now this is interesting. We have a `dev_t` device, which stands for device virtual pointer. It's the first block on the filesystem.
|
||||
|
||||
Cred means credentials, so that's if a process has credentials for the filesystem.
|
||||
|
||||
Below we have comments that say that this is logic for multiple mounts. Let's skip it.
|
||||
|
||||
```
|
||||
/*
|
||||
* Read the superblock from disk.
|
||||
*/
|
||||
error = bread(devvp, (daddr_t)(SBOFF / DEV_BSIZE), SBSIZE, &bp);
|
||||
if (error)
|
||||
goto out;
|
||||
fs = (struct ext2fs *)bp->b_data;
|
||||
error = e2fs_sbcheck(fs, ronly);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
ump = malloc(sizeof *ump, M_UFSMNT, M_WAITOK | M_ZERO);
|
||||
ump->um_e2fs = malloc(sizeof(struct m_ext2fs), M_UFSMNT,
|
||||
M_WAITOK | M_ZERO);
|
||||
```
|
||||
|
||||
Alright, finally some logic we can break apart! `bread` from `buffercache(9)` reads a block into the buffer specified- in this case the superblock. Then we read the superblock in, check for errors, good. Then malloc is called with some fancy flags, hmmm... its to hold the superblock in memory, but what are those?
|
||||
|
||||
This is kernel malloc after all, so we would be looking at `malloc(9)`. After looking at the man page, I can conlude that these are just special flags to do with memory security; the code is ok with sleeping until the buffer is allocated, and it will be zeroed out. Makes sense. `um_e2fs` probably stands for ufs memory ext2 filesystem, so its just allocating the in memory resources for us to use.
|
||||
|
||||
I'm starting to think that this part of the code is a bit irrelevant to us. But we do have to eventually read the entire (or the majority) of this code to gain a better understanding, so let's keep going.
|
||||
|
||||
```c
|
||||
/*
|
||||
* Copy in the superblock, compute in-memory values
|
||||
* and load group descriptors.
|
||||
*/
|
||||
e2fs_sbload(fs, &ump->um_e2fs->e2fs);
|
||||
if ((error = e2fs_sbfill(devvp, ump->um_e2fs)) != 0)
|
||||
goto out;
|
||||
brelse(bp);
|
||||
bp = NULL;
|
||||
fs = &ump->um_e2fs->e2fs;
|
||||
ump->um_e2fs->e2fs_ronly = ronly;
|
||||
ump->um_fstype = UM_EXT2FS;
|
||||
```
|
||||
|
||||
`e2fs_sbload` holds the bulk of our code here. Everything else is just closing the buffer and setting kernel flags, checking if it is clean, and so forth.
|
||||
|
||||
For the rest of the function, all we have is setting other EXT2 flags within the ufs moint point and setting other kernel parameters to work with the filesystem. With that, it's time to look at `e2fs_sbload`. This, alongside `e2fs_sbcheck` will probably be other functions we have to modify. We will also have to analyze the linux code for the same functions, although there we can do a skim and look for 64-bit mode. And for licensing issues, I don't believe that can be more thorough than one or two big quotes. We don't want a conflicting GPL license with the BSD license that we are already forced to implement for the OpenBSD source.
|
||||
|
||||
Turns out after some quick digging, `e2fs_sbload` is the macro we relied upon earlier that just transfers the bytes using `strcpy`. That's why we have to use `e2fs_sbcheck`; we have to validate the super block before we move it over.
|
||||
|
||||
```c
|
||||
/* This is called before the superblock is copied. Watch out for endianity! */
|
||||
static int
|
||||
e2fs_sbcheck(struct ext2fs *fs, int ronly)
|
||||
{
|
||||
u_int32_t mask, tmp;
|
||||
int i;
|
||||
|
||||
tmp = letoh16(fs->e2fs_magic);
|
||||
if (tmp != E2FS_MAGIC) {
|
||||
printf("ext2fs: wrong magic number 0x%x\n", tmp);
|
||||
return (EIO); /* XXX needs translation */
|
||||
}
|
||||
```
|
||||
|
||||
The mask must again be the same meaning instead of what we traditionally think of in "bitmask"; its the way bits are ordered in the filesystem. Then we have `letoh16(3)` for the byte orderings, which converts 16-bits little endian to a number they can use to check for the magic number. The good news is that we can also use these functions and we don't have to worry about how the underlying logic works that well.
|
||||
|
|
Loading…
Reference in New Issue