Nanjing University of Posts and telecommunications operating system experiment 4: simple file system simulation experiment

Keywords: C Operating System

Experiment purpose and requirements

Understand the file system composition and basic principle of the operating system, use these knowledge to simulate a FAT file system in memory, complete the file creation and indexing functions, and realize the following command interfaces:

        (1) New file, format: mkfile filename filecontent

                 filename: file name

                 filecontent: file content (characters)

                 Write FAT table, directory table and file content in FAT format.

        (2) List files, format: dir

                 List all file information and virtual disk information in the directory.

        (3) Display the file content in the format of type filename

                 filename: file name

                 Find the block number of the file name in the directory item, and print the file content on the screen.

(4) Delete file: del filename

                 filename: file name

                 Delete files and reclaim virtual disk space.

(5) Exiting the file system: quit

Experimental principle and content

Experimental principle:

File system refers to the physical space where files exist. Each partition in Linux system is a file system and has its own directory hierarchy. Linux will form the overall directory hierarchy of these separate file systems belonging to different partitions in a certain way. The operation of an operating system is inseparable from the operation of files, so it is necessary to own and maintain its own file system.

The Linux file system uses index nodes to record file information, which acts like the file allocation table of windows.

An index node is a structure that contains information such as the length of a file, creation and modification time, permissions, ownership, and location on disk. A file system maintains an array of inodes, and each file or directory corresponds to a unique element in the inode array. The system assigns a number to each index node, that is, the index number of the node in the array, which is called the index node number.

The Linux file system saves the file inode number and file name in the directory at the same time. A directory is just a table that combines the name of a file with its index node number. Each pair of file names and index node numbers in the directory are called a connection.

For a file, there is a unique inode number corresponding to it, but for an inode number, there can be multiple file names corresponding to it. Therefore, the same file on disk can be accessed through different paths.

The use case to be used in this experiment - fat. As a file name, each entry in fat (File Allocation Table) corresponds to the allocation status of each block in the disk. The number of entries in fat determines the maximum representable disk capacity. FAT16 indicates that up to 216 blocks (or clusters) are supported. The possible values of fat table entries are:

0x00: indicates that the corresponding block is idle

0xFF: indicates that the corresponding block is the last block of the file

Other values: indicates the block number of the next block of the file

The file control information is recorded in the file directory. Each directory item (FCB) corresponds to a file. When users want to operate the file, they always query the directory to find out the FCB, and then they can read the file content.

    Simple file system demonstration:

(1) Virtual disk structure

#define MAX_DISK_SIZE 256

u_char g_disk[MAX_DISK_SIZE]

If the size of each disk block is defined as 16 bytes, the system can store up to 16 disk block contents.

It is specified that the FAT table always occupies Block 0 and uses 8-bit FAT table entries, so there are 16 FAT table entries in Block 0, the directory table occupies blocks 1 to 4, and each directory entry (FCB) occupies 16 bytes. Therefore, the system supports up to 4 files. Since 0-5 blocks are occupied by the system, the 0th to 5th bytes of FAT should be 0xFF, so the maximum support capacity of the system is 10 blocks.

(2) System data structure

/*************************************************************

FAT structure

One byte (8 bits) is used to represent a block number

cluster value:

    0x00: idle

    0xFF: last block of file

    Other values: the next block number of the file

*************************************************************/

typedef struct _tagfat {

    u_char  cluster;

} FAT,*PFAT,**PPFAT;

/***********************************************************

File control block structure (20 bytes)

f_name  : The maximum length of the file name is 10 characters. Note that one character should be left to save '\ 0', so the maximum length of the file name is 9 characters

f_size   : file length

f_firstblock: the starting block number of the file

********************************************************/

typedef struct _tagfcb {

    char f_name[10];

    int f_size;

    int f_firstblock;

} FCB,*PFCB,**PPFCB;

(3) Principle of file deletion. When deleting a file, you need to do two things:

① Clear the FAT item corresponding to the block number occupied by the file 0x00. For example, if the deleted file occupies the 7th physical block, set the 7th byte of FAT to 0x00 (originally 0xFF)

② Set the first byte of the file name in the corresponding entry in the directory table to 0xE5

It can be seen that the file system using FAT does not clear the physical block content occupied by the file when deleting the file, which also makes it possible to de delete the file. In the directory item, the first byte of the file name is very important. If it is 0xE5, it indicates that the directory item is idle and can store a file information. Otherwise, it indicates that it has been occupied.

1. The source code of the core algorithm is given and detailed comments are added;

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//Define return value
#define	SUCCESS		1
#define DELETE_FAIL		0xE0
#define ALLOC_FAIL		0xE1

#ifndef _FILESYS
#define	_FILESYS

//For type definition, some types to be used below can be directly replaced in the form of reduction
typedef unsigned short u_short;
typedef unsigned short* pu_short;
typedef unsigned char u_char;
typedef unsigned char* pu_char;
typedef unsigned int u_int;
typedef unsigned int* pu_int;
typedef char* pchar;
typedef int* pint;
typedef short* pshort;

//File control block structure (20 bytes)
typedef struct _tagfcb 
{
	char f_name[10];		//file name
	int f_size;		//file length
	int f_firstblock;	//Starting block number
}FCB, *PFCB, **PPFCB;

/********************************************************************
FAT structure
 One byte (8 bits) is used to represent a block number, and up to 256 blocks can be represented
cluster Value:
	0x00 : free
	0xFF : The last piece of the file
	Other values: the next block number of the file
*********************************************************************/
typedef struct _tagfat
{
	u_char cluster;	
}FAT, *PFAT, **PPFAT;

typedef struct tagNode
{
	int iValue;
	struct tagNode *pNext;
}NODE, *PNODE;

/*************************************************************************
Simulated disk space

  Regulations: each block occupies 16 bytes. The virtual space used by the system is 1024 bytes, so the disk space is very large
		Divided into 64 blocks
  Block 0 stores the FAT table, with a maximum of 16 FAT table entries, which also means that the system can only support a maximum of 16 blocks
  Blocks 1 ~ 4 store directories, and each directory item occupies 1 block (16 bytes), indicating that the system supports up to 4 files
  Block 5 start storing file contents
**************************************************************************/
#define MAX_DISK_SIZE 1024
#define BLOCKSIZE 16
#define MAX_FAT	  16
extern u_char g_disk[MAX_DISK_SIZE];	//Simulated disk space
//Gets the first address of the specified block
void* getBlockAddr(int blockno);
//Virtual disk initialization
void InitDisk();
//create a file
int CreateFile(pchar f_name, pchar f_content, int f_size);
//List files
int ListFile();
//Display file
int displayFile(pchar filename);
//Delete file
int initList(PNODE* pListHead);	//Create a header node, iValue = 0
int insert(PNODE pListHead,int iValue);	//Insert a node with the value of iValue to the end of the linked list
int deleteList(PNODE pListHead);	//Delete a linked list space with a specified header pointer
void printList(PNODE pListHead);	//Print the entire linked list

#endif

int initList(PNODE* pListHead)
{
	//Create a head node space and assign a value of 0
	if ((*pListHead = (PNODE)malloc(sizeof(NODE))) == NULL)
	{
		return ALLOC_FAIL;
	}
	(*pListHead)->iValue = 0;
	(*pListHead)->pNext = NULL;
	return SUCCESS;
}

//Inserts a new node at the end of the linked list of the specified head node
int insert(PNODE pListHead,int iValue)
{
	PNODE	pNode;
	PNODE	pLastNode = pListHead;
	if ((pNode = (PNODE)malloc(sizeof(NODE))) == NULL)
	{
		return ALLOC_FAIL;
	}
	pNode->iValue = iValue;
	pNode->pNext = NULL;	
	while(1)
	{
		if (pLastNode->pNext == NULL)
		{
			pLastNode->pNext = pNode;
			break;
		}
		pLastNode = pLastNode->pNext;
	}
	return SUCCESS;
}

//Delete the entire linked list space, which is necessary at the end of the program
int deleteList(PNODE pListHead)
{
	PNODE pReadyToDelete = pListHead;
	while(pListHead == NULL)
	{
		pListHead = pListHead->pNext;
		free(pReadyToDelete);
		pReadyToDelete = pListHead;
	}
	return SUCCESS;
}

void printList(PNODE pListHead)
{
	PNODE pNode = pListHead;

	if (pNode->pNext == NULL)
	{
		printf("This LinkerList has no NODE except Header!\n");
		return;
	}
	printf("HEAD->");
	pNode = pNode->pNext;
	while(pNode != NULL)
	{
		printf("%d->",pNode->iValue);
		pNode = pNode->pNext;
	}
	printf("NULL\n");
	return;
}

u_char  g_disk[MAX_DISK_SIZE];//Disk space simulated with memory
const int	g_freedisksize = MAX_DISK_SIZE;	//Free space of virtual disk
const char	g_freeflag = (char)0xE5;

/********************************************************************
Gets the first address of the specified block
	blockno : Specifies the analog disk block number
	Therefore, the first address of the FAT table is getBlockAddr(0);
	The first address of the directory is getBlockAddr(1);
*********************************************************************/
void * getBlockAddr(int blockno)
{
	return (g_disk + blockno * BLOCKSIZE);
}

//Initialize virtual disk space
void InitDisk()
{
	PFCB pFCB = NULL;
	int i, j;
	memset(g_disk,0,MAX_DISK_SIZE);
	for (i = 1 ; i <= 4 ; i++)	//Set 4 directory entries to idle
	{
		pFCB = (PFCB)getBlockAddr(i);
		pFCB->f_name[0] = g_freeflag;
	}
	for (j = 0 ; j <= 4 ; j++)	//The first 5 bytes of the FAT table are set to FF, indicating that they have been occupied
	{
		((PFAT)(g_disk+j))->cluster = (u_char)0xFF;
	}
}

/********************************************************************
create a file
	f_name : File name pointer
	f_content : Pointer to the contents of the file
	f_size : file length
 Return value:
	0: normal
	10: By looking up the FAT table, not enough blocks were found to create the file
	11: All directory entries are used up. New files cannot be added.
		(By checking whether the first byte of the file name is E5, yes means idle)
	12: Error creating linked list
*********************************************************************/
int CreateFile(pchar f_name, pchar f_content, int f_size)
{
	PFCB pFCB = NULL; //Directory entry (FCB) pointer
	PFAT pFAT = (PFAT)getBlockAddr(0); //First address of FAT table
	int blockcount = 0;
	for(int i=1;i <= 4;i++) 
	{
		pFCB = (PFCB)getBlockAddr(i);
		if(pFCB->f_name[0] == g_freeflag)  //There are free directory entries
		{
			blockcount = (f_size / BLOCKSIZE) + 1;
			//Find out if there are enough free blocks in FAT, and use a linked list to record the number of free blocks to be occupied
			int k = blockcount;
			PNODE pListHead,pNode;
			if(initList(&pListHead) != SUCCESS)
			{
				return 12; //list initialize errno
			}
			for (int j=5; j < MAX_FAT ; j++)
			{
				if(((PFAT)(pFAT + j))->cluster == (u_char)0)
				{
					insert(pListHead,j);
					if(--k == 0)
						break;
				}
			}
			printList(pListHead);
			if (k > 0)
				return 10;	//file size error no

			//Next, start writing the file contents to the block and change the contents of the directory entry FCB
			strcpy(pFCB->f_name,f_name);					//Write file name
			pFCB->f_size = f_size;							//Write file size
			pFCB->f_firstblock = pListHead->pNext->iValue;	//File start block number
			int remainbyte = f_size; //How many bytes left to write into the block
			pNode = pListHead->pNext;
			for (k = 0 ; k < blockcount ; k++)
			{
				if (remainbyte <= BLOCKSIZE) //Less than one block
				{
					memcpy(getBlockAddr(pNode->iValue),
						f_content + (BLOCKSIZE * k),
						remainbyte);
				}
				else
				{
					memcpy(getBlockAddr(pNode->iValue),
						f_content + (BLOCKSIZE * k),
						BLOCKSIZE);
					remainbyte -= BLOCKSIZE;
				}
				pNode = pNode->pNext;
			}
			//Change the FAT table according to the free block number occupied by the file
			pNode = pListHead->pNext;
			while (1)
			{
				if (pNode->pNext == NULL)
				{
					((PFAT)(pFAT + pNode->iValue))->cluster = (u_char)0xFF;
					break;
				}
				((PFAT)(pFAT + pNode->iValue))->cluster = pNode->pNext->iValue;
				pNode = pNode->pNext;
			}
			//Reclaim linked list space
			deleteList(pListHead);
			return 0;
		}
	}
	return 11;//directory full error no
}

/********************************************************************
List catalog files
	  No parameters
 Return value:
	<0:Abnormal (this will not happen now)
	Other values: the number of files in the current directory
*********************************************************************/
int ListFile()
{
	PFCB pFCB = NULL;
	int filecount = 0; //File count
	int bytecount = 0; //Cumulative file size
	printf("My file system list:\n");
	printf("----------------------------------------------\n");
	printf("filename\t\t\t filesize\n");
	printf("----------------------------------------------\n");
	for (int i=1 ; i<=4 ; i++) 
	{
		pFCB = (PFCB)getBlockAddr(i);
		if (pFCB->f_name[0] != g_freeflag)
		{
			//display file info
			filecount++;
			bytecount += pFCB->f_size;
			printf("%s\t\t\t\t %d Bytes\n",pFCB->f_name,pFCB->f_size);
		}
	}
	if (filecount == 0)
	{
		printf("no file in this system now!\n");
	}
	printf("----------------------------------------------\n");
	printf("total files : %d \t total bytes : %d Bytes \n\t\t\t remain bytes : %d Bytes\n",
			filecount,bytecount,g_freedisksize-bytecount);
	return filecount;
}

/********************************************************************
Prints the characters of the specified byte
	  filecontent : File content pointer
	  size : Length of characters to print
 Return value: None
*********************************************************************/
void printc(pchar filecontent,int size)
{
	for(int i=0 ; i<size ; i++)
	{
		printf("%c",*filecontent);
		filecontent++;
	}
}
/********************************************************************
Show file contents
	  filename : file name
 Return value:
	0: Successful execution
	<0:Abnormal (this will not happen now)
*********************************************************************/
int displayFile(pchar filename)
{
	PFCB pFCB = NULL;
	PFAT pFAT = (PFAT)getBlockAddr(0);
	int remainbytes = 0;
	for (int i=1 ; i<=4 ; i++) 
	{
		pFCB = (PFCB)getBlockAddr(i);
		if (!strcmp(pFCB->f_name,filename))
		{
			remainbytes = pFCB->f_size;
			//Get the location of the first block of the file according to the starting block number in the FCB
			//Print out the contents of the first block
			printc((pchar)getBlockAddr(pFCB->f_firstblock),BLOCKSIZE);
			remainbytes -= BLOCKSIZE;
			//Then go to FAT to find the block number of subsequent blocks of the file until the end of FF description file is displayed in FAT
			int firstblock = pFCB->f_firstblock;
			while ( ((PFAT)(pFAT+firstblock))->cluster != (u_char)0xFF ) 
			{
				//It indicates that the file has subsequent blocks. Follow this instruction to print the contents of subsequent blocks
				printc((pchar)getBlockAddr(((PFAT)(pFAT+firstblock))->cluster),BLOCKSIZE);
				
				firstblock = ((PFAT)(pFAT+firstblock))->cluster;
				remainbytes -= BLOCKSIZE;
			}
			//Print last block
			printc((pchar)getBlockAddr(((PFAT)(pFAT+firstblock))->cluster),remainbytes);
			printf("\n");
			return 0; //Successful execution		
		}
	}
	return -1;//not found
}
/********************************************************************
Delete file
	  filename : file name
 Return value:
	0: Successful execution
	<0:Abnormal (this will not happen now)
*********************************************************************/
int deleteFile(pchar filename)
{
	PFCB pFCB = NULL;
	PFAT pFAT = (PFAT)getBlockAddr(0);
	for (int i=1 ; i<=4 ; i++) 
	{
		pFCB = (PFCB)getBlockAddr(i);
		if (!strcmp(pFCB->f_name,filename))
		{
			//Set the first byte of the file name of the directory entry FCB to E5
			pFCB->f_name[0] = g_freeflag;
			//Then set the block number occupied by the file in FAT to 00
			int firstblock = pFCB->f_firstblock;
			int nextblock = pFCB->f_firstblock;
			if (((PFAT)(pFAT+firstblock))->cluster == (u_char)0xFF) 
			{
				//This file only occupies one piece. Clear this item to 0
				((PFAT)(pFAT+firstblock))->cluster = (u_char)0x00;
			}
			else	//It indicates that the file occupies several blocks. Clear these FAT block indications to 0
			{
				while ( ((PFAT)(pFAT+nextblock))->cluster != (u_char)0xFF ) 
				{
					nextblock = ((PFAT)(pFAT+firstblock))->cluster;
					((PFAT)(pFAT+firstblock))->cluster = (u_char)0x00;
					firstblock = nextblock;
				}
				//Recycle the last piece of file
				((PFAT)(pFAT+nextblock))->cluster = (u_char)0x00;
			}
			printf("file deleted!\n");
			return 0; //Successful execution			
		}
	}
	return -1;//not found
}

int main()
{
	InitDisk(); //Initialize virtual disk space
	char filename[10];
	char filecontent[176];//The file content cannot exceed 176 B
	char command[10];
	int filesize = 0;
	int err_no = 0;
	printf("--------------------------------Tips--------------------------------\n");
	printf("                     The commonds of filesystem\n");
	printf("                       Create file: mkfile\n");
	printf("                       File directory: dir\n");
	printf("                       Display content: type\n");
	printf("                       Delete file: del\n");
	printf("                       Exit the system: quit\n");
	printf("---------------------------------------------------------------------\n");
	while(1)
	{
		printf("filesys>"); //Command prompt line
		scanf("%s",command);
		if (!strcmp(command,"mkfile")) //To create a file command, you need to provide a file name
		{
			printf("\ninput file name(<10 chars):");
			scanf("%s",filename);
			printf("\nReady to create file. Please input file content end with ENTER:\n");
			scanf("%s",filecontent);
			filesize = strlen(filecontent);
			if((err_no = CreateFile(filename,filecontent,filesize)) == 0)
			{
				printf("File created ! \n");
			}
			else
			{
				printf("Create failed! err_no=%d\n",err_no);
			}
		}
		else if (!strcmp(command,"dir")) //Column directory command
		{
			printf("The stored files are:\n");
			ListFile();
		}
		else if(!strcmp(command,"type")) //Show file contents command
		{
			printf("Please enter the file you want to view:\n");
			scanf("%s",filename);
			if (displayFile(filename) != 0 )
				printf("can't find the file you given!\n");
		}
		else if(!strcmp(command,"del")) //Delete file command
		{
			printf("Please enter the file you want to delete:\n");
			scanf("%s",filename);
			if (deleteFile(filename) != 0 )
				printf("can't find the file you given!\n");
		}
		else if (!strcmp(command,"quit")) //Exit this system command
		{
			break;
		}
		else
		{
			printf("Unknown command!\n");
		}				
	}
	return 0;
}

2. The test data, operation results and experimental conclusions are given.

Run the file simulation system program to display several common options, namely file creation, directory display, content display, file deletion and exit from the system.

1. File creation: enter the mkfile command. At this time, the system will prompt you to enter the file name and file content, and then create a test file and enter the content: hello. At the same time, write this file to the disk.

2. Display directory: enter the dir command, the file system displays the directory in the corresponding simulated disk, and you can see the corresponding file information stored in the disk.

3. Display content: enter the type command and prompt for the name of the file you want to view. You can see that entering test shows that the content stored in the test file is hello.

4. Delete file: enter del to prompt the name of the file you want to delete. If you enter the test file name, you can delete the test file in the file simulation system. You can see that there are no files in the file system by entering dir again.

The main functions of the whole file simulation system are as described above. After use, you can exit the system through the quit command.

Posted by DapperDanMan on Sat, 27 Nov 2021 19:45:00 -0800