Remove unpublished articles from master branch

This commit is contained in:
Thomas Lovén 2016-10-22 23:14:59 +02:00
parent ae0c4ee4ab
commit bf9e821772
3 changed files with 0 additions and 315 deletions

View File

@ -1,177 +0,0 @@
layout: post
title: "DITo - Framework"
subtitle: "the Disk Image TOols"
tags: [osdev, filesystems]
In my osdeving, I was starting to reach the point where a disk driver
seemed like the obvious next step. This was pretty much entirely unknown
territory for me. In fact, my only experience from disks and filesystems
were from when I got started in osdeving and found some tutorial in pdf
form which described how to write a bootloader in asm that read a kernel
from a FAT12 floppy disk.
Since then, whenever I needed a disk image for testing, I'd go through
a painful process of finding an image with GRUB preinstalled, mounting
it using a discontinued third party application, copy stuff to it, hope
I would be able to unmount it without the entire computer freezing up
and finally pray that it worked when I started the emulator. In short,
trying to manage a disk image from the command line in OSX sucks.
That's when I realized I could kill two birds with one stone. By writing
a tool for managing files in a disk image without mounting it, I could
gain understanding and experience of working with filesystems. If I
wrote it well, I would probably be able to reuse much of the code for
my kernel as well. At the time I had just finished my master thesis and
had all but signed the contract for my current employment, so I had some
free time on my hands while the paperwork fell through.
The result was [DITo - Disk Image
Tools](https://github.com/thomasloven/dito), a c library and set of
applications for creating and handling disk images from the command
line.
Recently, I actually did copy some of the code from DITo into my kernel.
Immagine my surprise when it actually worked like a charm after changing
only a few function calls.
I've since realized a couple of mistakes though, and decided to rewrite
some parts from scratch. Let's go!
###Drive operations
The basic operations of DITo are reading from or writing to image files
or disk drives. Each drive type has a driver
:::c
typedef struct drive_driver
{
int (*open)(struct drive_t *d, int flags);
int (*close)(struct drive_t *d, int flags);
int (*read)(struct drive_t *d, void *buffer, size_t length, off_t offset);
int (*write)(struct drive_t *d, void *buffer, size_t length, off_t offset);
} drive_driver_t;
The drive type contains a pointer to the driver and a pointer to some
arbitrary data used by the driver.
:::c
typedef struct drive_t
{
struct drive_driver *d;
void *data;
} drive_t;
Then there are some wrapper functions for performing the required
operations:
:::c
int drive_open(struct drive_t *d, int flags)
{
if(d->d->open)
return d->d->open(d, flags);
else
return 0;
}
and simmilar for `drive_close`, `drive_read` and `drive_write`.
###Filesystem operations
The next important part of DITo is the filesystem handling. After
thinking about it, the important primitive functions for all file
operations I could think about are all in a filesystem driver struct:
:::c
struct fs_driver
{
INODE (*open)(struct fs_t *fs, const char *path, int flags);
int (*close)(struct fs_t *fs, INODE ino);
int (*read)(struct fs_t *fs, INODE ino, void *buffer, size_t length, off_t offset);
int (*write)(struct fs_t *fs, INODE ino, void *buffer, size_t length, off_t offset);
int (*truncate)(struct fs_t *fs, INODE ino, off_t length);
int (*stat)(struct fs_t *fs, INODE ino, struct stat *st);
int (*touch)(struct fs_t *fs, const char *path, struct stat *st);
int (*link)(struct fs_t *fs, const char *path1, const char *path2);
int (*unlink)(struct fs_t *fs, const char *path);
dirent_t *(*readdir)(struct fs_t *fs, INODE dir, unsigned int num);
};
The `fs_t` type contains a pointer to the driver, a pointer to the drive
and a general data pointer.
:::c
typedef struct fs_t
{
struct fs_driver *driver;
drive_t *d;
void *data;
} fs_t;
The wrapper functions `fs_open`, `fs_close` and so on work the same way
as the `drive_*` functions.
The `INODE` type is a pointer to a struct containing a pointer to the
filesystem, a unique inode number and a pointer to arbitrary data.
:::c
struct ino_st
{
fs_t *fs;
unsigned int ino;
void *data;
};
typedef struct ino_st * INODE;
And that's the basic framework. As you probably notice, the same `fs_t`
pointer is passed to most functions twice. Once as `fs` and once as
`ino->fs`. I decided to keep it this way to get the function interface
consistant, and also for the possible sanity check `fs == ino->fs`.
The idea behind the framework is that the same functions should be
usable for all kinds of filesystems on all kinds of drives. For example,
if I have one image of an FAT floppy disk with a file I want copied to
the ext2 formated second partition of a hard drive image, I could do
someting like this:
:::c
drive_t *fat_disk = image_drive("floppy.img");
drive_open(fat_disk, READ_FLAG);
drive_t *ext2_disk = image_drive("harddrive.img");
drive_open(ext2_disk, READ_WRITE_FLAG);
drive_t *ext2_partition = mbr_drive(ext2_disk, 2);
drive_open(ext2_partition, READ_WRITE_FLAG);
fs_t *fat = fat_fs(fat_disk);
fs_t *ext2 = ext2_fs(ext2_partition);
INODE source = fs_open(fat, "/path/to/file", READ_FLAG);
struct st *st = malloc(sizeof(struct st));
fs_struct(fat, source, st);
fs_touch(ext2, "/new/path", st);
INODE destination = fs_open(ext2, "/new/path", WRITE_FLAG);
void *buffer = malloc(BUFER_SIZE);
off_t offset = 0;
off_t add = 0;
while(add = fs_read(fat, source, buffer, BUFFER_SIZE, offset))
{
fs_write(ext2, destination, buffer, BUFFER_SIZE, offset);
offset += add;
}
fs_close(destination);
fs_close(source);
drive_close(ext2_partition);
drive_close(fat_disk);
Which of couse will eventually become its own tool so that the actual
work the end user has to do becomes:
:::bash
$ dito-cp floppy.img:/path/to/file harddrive.img:2:/new/path

View File

@ -1,130 +0,0 @@
layout: post
title: "DITo - Drives"
subtitle: "Exploring the MBR"
tags: [osdev, filesystems]
Let's write a few drive drivers.
### Pure image
The simplest image we'd have to handle is a plain image file, such as
a floppy disk. There's only a single partition and a one-to-one mapping
from the image to the drive.
First of all, let's think of what data we'd need to store. The filename
might be nice to have, and probably a pointer to the opened file. Would
hate to lose that...
:::c
struct im_data
{
char *filename;
FILE *file;
};
The functions required for the driver would then make use of the
c standard library:
:::c
int im_open(drive_t *d, int flags)
{
struct im_data *data = d->data;
data->file = fopen(data->filename, flags);
return 1;
}
int im_close(drive_t *d, int flags)
{
struct im_data *data = d->data;
fclose(data->file);
return 1;
}
int im_read(drive_t *d, void *buffer, size_t length, off_t offset)
{
struct im_data *data = d->data;
fseek(data->file, offset, SEEK_SET);
return fread(buffer, length, 1, data->file);
}
int im_write(drive_t *d, void *buffer, size_t length, off_t offset)
{
struct im_data *data = d->data;
fseek(data->file, offset, SEEK_SET);
return fwrite(buffer, length, 1, data->file);
}
The function for setting up the drive is equally simple:
:::c
drive_driver_t im_driver =
{
im_open,
im_close,
im_read,
im_write,
};
drive_t *image_drive(const char *filename)
{
drive_t *d = calloc(1, sizeof(drive_t));
struct im_data *data = calloc(1, sizeof(struct im_data));
d->driver = &im_driver;
d->data = data;
data->filename = strndup(filename, FILENAME_MAX_LENGTH);
return d;
}
As always, I've stripped away all sanity checking and error handling.
###MBR formated disks
In order to use a harddrive for different things (like file storage or
swap space), it can be split into several partitions. One standard for
doing this is through a table in the MBR or Master Boot Record of the
disk.
The MBR starts with 436 bytes of space for a bootloader, then there's a
10 byte disk ID followed by 4 partition table entries.
The entries of the table has the following structure:
:::c
struct MBR
{
uint8_t boot_indicator;
uint8_t start_chs[3];
uint8_t system_id;
uint8_t end_chs[3];
uint32_t start_lba;
uint32_t num_sectors;
}__attribute__((packed));
_CHS_ and _LBA_ are different ways of addressing sectors of the disk.
CHS - or Cylinder Sector Head - addresses the sectors by their physical
location on the disk. The format in this table is first 8 bytes for
the wanted head (two heads per harddrive platter), then 6 bits for the
sector (angle on the platter) and finally 10 bits for the cylinder
(distance from platter center). This method requires knowledge of the
actual physical structure of the drive and maxes out at around 8 GB with
512 byte sectors.
LBA - or Logical Block Addressing - leaves all that to the drive and
addresses the sectors by a single number. With 512 byte sectors, this is
useful up to about 2 GB.
For our purposes, the fields `start_lba` and `num_sectors` are all we
need (and `system_id` to check if the partition table entry is used).
###MBR driver
The driver for reading from a partition is pretty alike the plain image
driver. Principally, the only difference is that 512 times `start_lba`
has to be added to the offset for reading and writing, and that care
must be taken not to allow reading or writing outside of the partition
boundaries.
I even wrote the driver to piggyback on a different driver. I.e. the
function for creating the drive object requires a drive object as
argument (as in the [example in the previous
post](/blog/2014/04/Dito-Framework/)).

View File

@ -1,8 +0,0 @@
layout: post
title: "DITo - Ext2"
subtitle: "A beginning..."
tags: [osdev, filesystems]
It's finally time to start looking at an actual filesystem.
I chose to begin with ext2.