리눅스 커널 내부구조 10장 #4 FAT File System (filesystem)

문연수·2022년 3월 1일
0

iamroot (Linux Internal)

목록 보기
19/24

shell_filesystem 코드는 shellFAT 사이를 이어주는 징검다리이다. shell 측에선 추상화된 filesystem 의 코드를 호출하고 filesystem 은 실질적인 구현인 FAT 의 코드를 호출하게 된다.

 이러한 방식이 상당히 복잡하고 또 불필요하다고 느낄 수 있겠지만 이러한 과정을 거치게 되면 하나의 파일 시스템에서 다른 파일 시스템으로 변경(예를 들어 FAT 에서 ext 로의 변경) 하더라도 shell 코드는 그 어떠한 변경 없이 그대로 가져다 쓸 수 있다는 큰 장점이 있다. 이것이 바로 추상화의 아름다움이라 할 수 있겠다.

1. shell_filesystem.h

#ifndef SHELL_FILESYSTEM_H__
#define SHELL_FILESYSTEM_H__

#include "disksim.h"
#include "shell_entry.h"

struct shell_fs_operations;

struct shell_file_operations {
	int (*create)(
			struct disk_operations *, struct shell_fs_operations *,
			const struct shell_entry *, const char *,
			struct shell_entry *
	);

	int (*remove)(
			struct disk_operations *, struct shell_fs_operations *,
			const struct shell_entry *, const char *
	);

	int (*read)(
			struct disk_operations *, struct shell_fs_operations *,
			const struct shell_entry *, const struct shell_entry *,
			unsigned long, unsigned long, char *
	);

	int (*write)(
			struct disk_operations *, struct shell_fs_operations *,
			const struct shell_entry *, struct shell_entry *,
			unsigned long, unsigned long, const char *
	);
};

struct shell_fs_operations {
	int (*read_dir) (
			struct disk_operations *, struct shell_fs_operations *,
			const struct shell_entry *, struct shell_entry_list *
	);

	int (*stat) (
			struct disk_operations *, struct shell_fs_operations *,
			unsigned int *, unsigned int *
	);

	int (*mkdir) (
			struct disk_operations *, struct shell_fs_operations *,
			struct shell_entry *, const char *, struct shell_entry *
	);

	int (*rmdir) (
			struct disk_operations *, struct shell_fs_operations *,
			const struct shell_entry *, const char *
	);

	int (*lookup)(
			struct disk_operations *, struct shell_fs_operations *,
			const struct shell_entry *, struct shell_entry *,
			const char *
	);

	struct shell_file_operations file_ops;
	void *pdata;
};

struct shell_filesystem {
	char *name;
	int (*mount) (
		struct disk_operations *, struct shell_fs_operations *,
		struct shell_entry *
	);
	void (*umount) (struct disk_operations *, struct shell_fs_operations *);
	int (*format) (struct disk_operations *, void *);
};

void shell_filesystem_register(struct shell_filesystem *);

#endif

 앞서 shell.c 코드를 설명할 때 shell_create() 에서 fops 는 초기화되지 않았는데 이는 mount() 가 호출됨에 따라 다시 초기화가 이뤄지게 된다.

2. shell_filesystem.c

// -----------------------------------------------------------------------------
// source file macro
// -----------------------------------------------------------------------------
#define _POSIX_C_SOURCE 200809L

// -----------------------------------------------------------------------------
// include own header
// -----------------------------------------------------------------------------
#include "shell_filesystem.h"

// -----------------------------------------------------------------------------
// include user-defined header
// -----------------------------------------------------------------------------
#include "fat.h"

// -----------------------------------------------------------------------------
// include c standard library
// -----------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// -----------------------------------------------------------------------------
// local function
// -----------------------------------------------------------------------------
static char *stpncpy_ex(char *dest, char *orig, char *stop, int size)
{
	while ( ( *orig != EOF                  ) 
	&&      ( (strchr(stop, *orig)) == NULL )
	&&      ( (size--) > 0                  ) )
		*dest++ = *orig++;

	return dest;
}
static int fat_entry_to_shell_entry(
		const struct fat_node *fat_entry,
		struct shell_entry *shell_entry
) {
	struct fat_node *entry = (struct fat_node *) shell_entry->pdata;
	char *str;

	memset(shell_entry, 0x00, sizeof(struct shell_entry));
	if (entry->entry.attribute != FAT_ATTR_VOLUME_ID) {
		str = shell_entry->name;
		str = stpncpy_ex(str, (char *) fat_entry->entry.name, " ", 8);
		if (fat_entry->entry.name[8] != 0x20) {
			str = stpncpy_ex(str, ".", " ", 1);
			str = stpncpy_ex(
				str, (char *) &fat_entry->entry.name[8], " ", 3
			);
		}
	}

	if (fat_entry->entry.attribute & FAT_ATTR_DIRECTORY
	||  fat_entry->entry.attribute & FAT_ATTR_VOLUME_ID) 
	{
		shell_entry->is_dir = 1;
	} else {
		shell_entry->size = fat_entry->entry.filesize;
	}

	*entry = *fat_entry;

	return 0;
}

static int fs_create(
		struct disk_operations *disk, struct shell_fs_operations *fops,
		const struct shell_entry *parent, const char *name,
		struct shell_entry *ret
) {
	struct fat_node *fat_parent, fat_entry;
	int result;

	fat_parent = (struct fat_node *) parent->pdata;
	result = fat_create(fat_parent, name, &fat_entry);
	*((struct fat_node *) ret->pdata) = fat_entry;

	return result;
}

static int fs_remove(
		struct disk_operations *disk, struct shell_fs_operations *fops,
		const struct shell_entry *parent, const char *name
) {
	struct fat_node file;

	if (fat_lookup((struct fat_node *) parent->pdata, name, &file) != 0)
		return -1;

	return fat_remove(&file);
}

static int fs_read(
		struct disk_operations *disk, struct shell_fs_operations *fops,
		const struct shell_entry *parent,
		const struct shell_entry *entry,
		unsigned long offset ,unsigned long length, char *buffer
) {
	return fat_read((struct fat_node *) entry->pdata, offset, length, buffer);
}

static int fs_write(
		struct disk_operations *disk, struct shell_fs_operations *fops,
		const struct shell_entry *parent, struct shell_entry *entry,
		unsigned long offset, unsigned long length, const char *buffer
) {
	return fat_write((struct fat_node *) entry->pdata, offset, length, buffer);
}

static int fs_stat(
		struct disk_operations *disk, struct shell_fs_operations *fops,
		unsigned int *total_sectors, unsigned int *used_sectors
) {
	return fat_df(
		(struct fat_filesystem *) (fops->pdata),
		total_sectors,
		used_sectors
	);
}

static int adder(void *list, struct fat_node *entry)
{
	struct shell_entry_list *entry_list = (struct shell_entry_list *) list;
	struct shell_entry new_entry;

	fat_entry_to_shell_entry(entry, &new_entry);
	shell_entry_list_add(entry_list, &new_entry);

	return 0;
}

static int fs_read_dir(
		struct disk_operations *disk, struct shell_fs_operations *fops,
		const struct shell_entry *parent, struct shell_entry_list *list
) {
	struct fat_node entry;
	if (list->count)
		shell_entry_list_release(list);

	entry = *((struct fat_node *) parent->pdata);
	fat_read_dir(&entry, adder, list);

	return 0;
}

static bool is_exist(
		struct disk_operations *disk, struct shell_fs_operations *fops,
		const struct shell_entry *parent, const char *name)
{
	struct shell_entry_list list;
	bool find = false;

	shell_entry_list_init(&list);
	fs_read_dir(disk, fops, parent, &list);

	LIST_ITERATOR_WITH_ENTRY(list.head, entry, struct shell_entry, list)
		if (strncmp(entry->name, name, 12) == 0) {
			find = true;
			LIST_ITERATOR_BREAK;
		}
	LIST_ITERATOR_END

	shell_entry_list_release(&list);
	return find;
}

static int fs_mkdir(
		struct disk_operations *disk, struct shell_fs_operations *fops,
		struct shell_entry *parent, const char *name,
		struct shell_entry *ret
) {
	struct fat_node fat_parent;
	struct fat_node fat_entry;
	int result;

	if (is_exist(disk, fops, parent, name))
		return -1;

	fat_parent = *((struct fat_node *) parent->pdata);
	result = fat_mkdir(&fat_parent, name, &fat_entry);
	fat_entry_to_shell_entry(&fat_entry, ret);

	return result;
}

static int fs_rmdir(
		struct disk_operations *disk, struct shell_fs_operations *fops,
		const struct shell_entry *parent, const char *name
) {
	struct fat_node fat_parent;
	struct fat_node dir;

	fat_parent = *((struct fat_node *) parent->pdata);
	fat_lookup(&fat_parent, name, &dir);

	return fat_rmdir(&dir);
}

static int fs_lookup(
		struct disk_operations *disk, struct shell_fs_operations *fops,
		const struct shell_entry *parent, struct shell_entry *entry,
		const char *name
) {
	struct fat_node fat_parent, fat_entry;
	int result;

	fat_parent = *((struct fat_node *) parent->pdata);
	result = fat_lookup(&fat_parent, name, &fat_entry);
	fat_entry_to_shell_entry(&fat_entry, entry);

	return result;
}
/*******************************************************************************
 * export function
 ******************************************************************************/
int fs_mount(
		struct disk_operations *disk, struct shell_fs_operations *fops, 
		struct shell_entry *root)
{
	struct fat_filesystem *fat;
	struct fat_node fat_entry;
	int result;
	char vol_label[12] = { 0, };

	*fops = (struct shell_fs_operations) {
		.read_dir	= fs_read_dir,
		.stat		= fs_stat,
		.mkdir		= fs_mkdir,
		.rmdir		= fs_rmdir,
		.lookup		= fs_lookup,
		.file_ops	= (struct shell_file_operations) {
			.create	 = fs_create,
			.read	 = fs_read,
			.write	 = fs_write,
			.remove	 = fs_remove
		},
		.pdata		= NULL
	};

	
	fops->pdata = malloc(sizeof(struct fat_filesystem));
	if (fops->pdata == NULL)
		return -1;

	fat = (struct fat_filesystem *) fops->pdata;
	memset(fat, 0x00, sizeof(struct fat_filesystem));
	fat->disk = disk;
	result = fat_read_superblock(fat, &fat_entry);
	if (result == 0) {
		if (fat->type == FAT_TYPE_FAT16)
			memcpy(vol_label, fat->bpb.bpb32.bs.volume_label, 11);
		else
			memcpy(vol_label, fat->bpb.bs.volume_label, 11);

		printf("%-12s: %s\n", "FAT type",
				      fat_type_to_string(fat->type));
		printf("%-12s: %s\n", "volume label", vol_label);
		printf("%-12s: %d\n", "bytes per sector",
				      fat->bpb.bytes_per_sector);
		printf("%-12s: %d\n", "sectors per cluster",
				      fat->bpb.sectors_per_cluster);
		printf("%-12s: %d\n", "number of FATs",
				      fat->bpb.number_of_fats);
		printf("%-12s: %d\n", "root entry count",
				      fat->bpb.root_entry_count);
		printf("%-12s: %d\n", "total sectors",
			(fat->bpb.total_sectors ? fat->bpb.total_sectors
						: fat->bpb.total_sectors32));
		putchar('\n');
	}

	fat_entry_to_shell_entry(&fat_entry, root);

	return result;
}

void fs_umount(struct disk_operations *disk, struct shell_fs_operations *fops)
{
	if (fops && fops->pdata) {
		fat_umount((struct fat_filesystem *) fops->pdata);
		free(fops->pdata);
		fops->pdata = 0;
	}
}

int fs_format(struct disk_operations *disk, void *param)
{
	char *fat_type_string[3] = {
		"FAT12", "FAT16", "FAT32"
	};
	enum fat_type fat_type[3] = {
		FAT_TYPE_FAT12, FAT_TYPE_FAT16, FAT_TYPE_FAT32
	};
	int type_idx;
	char *param_str = (char *) param;

	if (param) {
		int i;
		for (i = 0; i < 3; i++) {
			if (strncmp(param_str, fat_type_string[i], 100) == 0) {
				type_idx = i;
				break;
			}
		}

		if (i == 3) {
			printf("unkown FAT type\n");
			return -1;
		}
	} else {
		if (disk->number_of_sectors <= 8400)
			type_idx = 0;
		else if (disk->number_of_sectors <= 66600)
			type_idx = 1;
		else
			type_idx = 2;
	}

	printf("formatting as a %s\n", fat_type_string[type_idx]);

	return fat_format(disk, fat_type[type_idx]);
}
/*******************************************************************************
 * export function
 ******************************************************************************/
void shell_register_filesystem(struct shell_filesystem *fs)
{
	*fs = (struct shell_filesystem) {
		.name = "FAT",
		.mount = fs_mount,
		.umount = fs_umount,
		.format = fs_format
	};
}

 파일 시스템 코드 역시 결국 FAT 측의 함수로 advance 되기에 더 설명할 부분이 없다. 눈 여겨볼만한 부분은 fat_node 구조체와 shell_entry 구조체를 섞어 쓰고 있는 부분이다. 이러한 구조를 채택한 이유는 filesystem 의 동작을 shell_entry 로 고정하면 파일 시스템이 변경 되더라도 각각의 filesystem 의 데이터를 추출해 shell_entry 만 잘 채워넣으면 되기 때문이다. 그래서 fat_entry_to_shell_entry() 라는 메서드가 존재하는 것이다.

ls 명령어의 동작 또한 shell_entry_list 구조체를 통해 동작하게 된다. fat_node 로 이어진 연결 리스트가 아니다. 디렉터리 내의 파일들을 탐색하는 측은 FAT 이기에 연결 리스트를 구성하기 위한 adder 함수를 fat 측으로 포워딩한다.

profile
2000.11.30

0개의 댓글