Linux kernel: file system and super block

catalogue

Part I: file system module template

5 groups of system calls

Hard coded SFS superblock and root inode

Bare bone # SFS module is running

Part II: file system on block device

Real SFS on block device

Part III: running file system

In actual SFS operation

Implementation behind running

note

 

Part I: file system module template

This is the 22nd article, part of the Linux device driver series, which introduces a basic file system module.

https://sysplay.in/blog/linux-device-drivers/2014/11/the-semester-project-part-v-file-system-module-template/

Through the formatting of pen drive, the file system is all set in hardware space. Now it's your turn to decode using the corresponding file system module in kernel space and provide a user space file system interface accordingly to browse it like other file systems.

5 groups of system calls

Different from character or block drivers, file system drivers involve not only a structure of function pointers of various interfaces provided by the file system, but also the structure of five function pointers. These are:

  • struct file_system_type – contains functions that operate on superblocks
  • struct super_operations – contains functions that operate on inode s
  • struct inode_operations – contains functions that operate on directory entries
  • struct file_operations – contains functions to operate on file data (through page caching)
  • struct address_space_operations – page cache operations that contain file data

With these, there are many new terms. He referred to the following glossary to understand the various terms used above and later in the development of file system modules:

  • Page cache or buffer cache: RAM buffer pool, each page size (usually 4096 bytes). These buffers are used as buffers for file data read from the underlying hardware, thereby improving the performance of file operations
  • Inode: structure containing file metadata / information, such as permission, owner, etc. Although the file name is the metadata of the file, in order to make better use of space, in a typical Linux file system, it is not saved in inode. In the so-called directory entry. The collection of inodes is called the inode table
  • Directory entry: a structure containing the name and inode number of a file or directory. In a typical Linux based file system, the data block of directory D stores the direct files of directory D and the collection of directory entries of directory D.
  • Super block: a structure that contains information about various data structures of the file system, such as inode table,... Is basically metadata, that is, metadata of metadata
  • Virtual file system (VFS): the conceptual file system layer connects kernel space and user space in an abstract way, displays "all contents" as files, and converts its operation from users to appropriate entities in kernel space

Each of the above five structures contains a function pointer list, which needs to be filled according to all functions existing in the file system (module) or supported functions. For example,

  1. struct file_system_type may contain system calls for mounting and unmounting file systems, basically running on its superblock;

  2. struct super_operations may include inode read / write system calls;

  3. struct inode_operations may include the function of finding directory entries;

  4. struct file_operations can usually operate on the file data cached on the page, and then can be called in struct address_ space_ Page cache operations defined in operations.

For these various operations, most of these functions will interface with the corresponding base block device driver to eventually run with the formatted file system in hardware space.

First, Pugs shows the complete framework of his real SFS module, but with the least functions, it is enough to compile, load and not crash the kernel. He only fills in the first of the five structures - struct file_system_type ; The rest is empty. This is the exact code of the structure definition:

#include <linux/fs.h> /* For system calls, structures, ... */

static struct file_system_type sfs;
static struct super_operations sfs_sops;
static struct inode_operations sfs_iops;
static struct file_operations sfs_fops;
static struct address_space_operations sfs_aops;
#include <linux/version.h> /* For LINUX_VERSION_CODE & KERNEL_VERSION */

static struct file_system_type sfs =
{
	name: "sfs", /* Name of our file system */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38))
	get_sb:  sfs_get_sb,
#else
	mount:  sfs_mount,
#endif
	kill_sb: kill_block_super,
	owner: THIS_MODULE
};

Note that before Linux kernel version 2.6.38, the function pointer installed was called get_sb, and its parameters used to be slightly different. Therefore, the above #if makes it at least 2.6.3x and possibly 3.3 Compatibility between X kernel versions - other versions cannot be guaranteed. Therefore, the corresponding function sfs_get_sb () and sfs_mount() is also if'd as follows:

#include <linux/kernel.h> /* For printk, ... */

if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38))
static int sfs_get_sb(struct file_system_type *fs_type, int flags,
			const char *devname, void *data, struct vfsmount *vm)
{
	printk(KERN_INFO "sfs: devname = %s\n", devname);

	/* sfs_fill_super this will be called to fill the super block */
	return get_sb_bdev(fs_type, flags, devname, data, &sfs_fill_super, vm);
}
#else
static struct dentry *sfs_mount(struct file_system_type *fs_type,
					int flags, const char *devname, void *data)
{
	printk(KERN_INFO "sfs: devname = %s\n", devname);

	/* sfs_fill_super this will be called to fill the super block */
	return mount_bdev(fs_type, flags, devname, data, &sfs_fill_super);
}
#endif

The only difference between the above two functions is that in the later functions, the structure related to VFS installation point has been deleted. printk () will display the device file of the basic partition that the user will mount, which is basically the SFS formatted partition of the pen drive. get_sb_bdev() and mount_bdev () is the general block device installation function of the corresponding kernel version. They are in FS / super C and in < Linux / Fs h> Prototype in. Like most other file system writers, pugs uses them. Do you want to know that all file systems mount block devices in the same way? Except that the installation operation needs to fill the part of the VFS super block structure (struct super_block), depending on the super block of the underlying file system - obviously, it is likely to be different. But what should we do? Please observe carefully that in the above function, in addition to passing all parameters as is, there is an additional parameter sfs_fill_super, a custom function of Pugs, is used to fill the super block of VFS according to the SFS file system.

Unlike the mount function pointer, the unmount function pointer is the same for some kernel versions (kill_sb). When unmounting, there is not even the smallest difference across different file systems. Therefore, the generic block device unload function kill_block_super() has been used directly as a function pointer.

In SFS_ fill_ In super (), ideally, Pugs should read the super block from the underlying hardware space SFS, and then convert and fill it into the super block of VFS accordingly, so that VFS can provide user space file system interface. But he hasn't figured out how to read the underlying block device in kernel space. Among the information obtained from the mount command issued by the user, the block device information that has been used has been embedded into super_ The block structure itself. But when Pugs decided to build a real SFS, first, he continued to write the sfs_super_fill() can also be used as a hardcoded fill function. In this regard, he registered the Simula file system with VFS. Like any other Linux driver, this is the constructor and destructor of the file system driver:

#include <linux/module.h> /* For module related macros, ... */

static int __init sfs_init(void)
{
	int err;

	err = register_filesystem(&sfs);
	return err;
}

static void __exit sfs_exit(void)
{
	unregister_filesystem(&sfs);
}

module_init(sfs_init);
module_exit(sfs_exit);

Existing register_filesystem code () and unregister_ File system() gets the structure of the pointer file_system_type SFS (solid above) because of their parameters to register and unregister the file system described by it, respectively.

 

Hard coded SFS superblock and root inode

Yes, this is hard coded sfs_fill_super() function:

#include "real_sfs_ds.h" /* For SFS related defines, data structures, ... */

static int sfs_fill_super(struct super_block *sb, void *data, int silent)
{
	printk(KERN_INFO "sfs: sfs_fill_super\n");

	sb->s_blocksize = SIMULA_FS_BLOCK_SIZE;
	sb->s_blocksize_bits = SIMULA_FS_BLOCK_SIZE_BITS;
	sb->s_magic = SIMULA_FS_TYPE;
	sb->s_type = &sfs; // file_system_type
	sb->s_op = &sfs_sops; // super block operations

	sfs_root_inode = iget_locked(sb, 1); // obtain an inode from VFS
	if (!sfs_root_inode)
	{
		return -EACCES;
	}
	if (sfs_root_inode->i_state & I_NEW) // allocated fresh now
	{
		printk(KERN_INFO "sfs: Got new root inode, let's fill in\n");
		sfs_root_inode->i_op = &sfs_iops; // inode operations
		sfs_root_inode->i_mode = S_IFDIR | S_IRWXU |
			S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
		sfs_root_inode->i_fop = &sfs_fops; // file operations
		sfs_root_inode->i_mapping->a_ops = &sfs_aops; // address operations
		unlock_new_inode(sfs_root_inode);
	}
	else
	{
		printk(KERN_INFO "sfs: Got root inode from inode cache\n");
	}

#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,4,0))
	sb->s_root = d_alloc_root(sfs_root_inode);
#else
	sb->s_root = d_make_root(sfs_root_inode);
#endif
	if (!sb->s_root)
	{
		iget_failed(sfs_root_inode);
		return -ENOMEM;
	}

	return 0;
}

As mentioned earlier, the function should basically read the underlying SFS superblock and transform and populate the structure super accordingly_ Block, which is pointed to by its first parameter sb. Therefore, understanding it is different from understanding struct super_ The minimum field of block has been filled in. The first three are the block size, the base 2 logarithm, and the type / magic code of the Simula file system. As Pugs further encodes, we will see that once he obtains the super block from the hardware space, he will instead obtain these values from that super block, and more importantly, verify them to ensure that the correct partition is installed.

Then, various structure pointers are pointed to their corresponding function pointer structures. Last but not least, the pointer s to the root inode_ Root refers to the struct index structure obtained from the index node cache of VFS. It is based on the index node number of the root node (hard coded as 1 at present), which may be changed. If the inode structure is obtained for the first time, that is, it is obtained for the first time, fill it according to the content of the underlying SFS root inode. Similarly, the mode field is hard coded as "drwxr-xr-x". In addition, the usual structure pointer is initialized by the corresponding structure address. Finally, attach the inode of the root to the superblock with the following command: d_alloc_root () or d_make_root (), depending on the kernel version.

All the above code snippets are put together as bare bone real_sfs_bb.c. And along real_sfs_ds.h (previously created based on the same file), and the support generation file can be downloaded from rsfsbb_code.tbz2.

 

Bare bone # SFS module is running

Compile with make and get real_ sfs_ bb. After Ko driver, Pugs conducted his usual unusual experiment, as shown in Figure 38.

Figure 38: Bare-bone real SFS experiments

Pugs experiment (illustration of figure 38):

  • Check the file systems supported by the kernel in the kernel window / proc / filesystems
  • Loaded real_sfs_bb.ko driver
  • Recheck the kernel window / proc / filesystems for the file systems supported by the kernel. Now it displays the sfs listed at the end
  • A / dev / sdb1 to / mnt that does not have its pen drive partition installed uses the SFS file system. Check dmesg logs on adjacent windows. (remember, at this point, sfs_fill_super() does not actually read the partition, so no checks are made.). Therefore, the format of / dev / sdb1 is not really important.) However, yes, the mount, which uses the mount output to display the SFS file system

too bad! However, df output shows "function not implemented" and cd shows "not a directory". ah Pugs has not implemented any other function in any of the other four function pointer structures. Therefore, this is expected.

Note: the above experiments use "sudo". Instead, you can enter the root shell and perform the same operation without "sudo".

OK, so there is no kernel crash and no bare metal file system running - Yippee.!! Pugs know that df, cd,... Has not yet worked. To do this, he needs to start adding various system calls to the other (four) function pointer structures so that he can browse coldly with various shell commands like all other file systems. Yes, pugs has started his mission - after all, he needs to make a weird presentation for his last semester project.

 

 

Part II: file system on block device

This twenty third article, part of the Linux device driver series, enhances previously written bare metal file system modules to connect to real hardware partitions

https://sysplay.in/blog/linux-device-drivers/2014/12/the-semester-project-part-vi-file-system-on-block-device/

Since the last bare file system, the first thing Pugs has figured out is how to read data from the underlying block device. The following are typical ways to do this:

struct buffer_head *bh;

bh = sb_bread(sb, block); /* sb is the struct super_block pointer */
// bh->b_data contains the data
// Once done, bh should be released using:
brelse(bh);

In order to complete the above operations and other actual SFS (Simula file system) operations, Pugs believes that it is necessary to take his own handle as a key parameter. He added this content to the following content (in the previous real_sfs_ds.h):

typedef struct sfs_info
{
	struct super_block *vfs_sb; /* Super block structure from VFS for this fs */
	sfs_super_block_t sb; /* Our fs super block */
	byte1_t *used_blocks; /* Used blocks tracker */
} sfs_info_t;

The main idea behind it is to put all the necessary static global variables in a single structure and point them out through the private data pointer of the file system (that is, the s_fs_info pointer in the super_block structure). So, fill_ super_ The key of block () (in the previous real_sfs_bb.c file) is changed to:

  • Use kzalloc() to allocate a structure for the handle
  • Initialize the structure of the handle (through init_browsing())
  • Read the physical super block, verify and convert its information into VFS super block (through init_browsing())
  • Use s_fs_info points to it (via init_browsing())
  • Update the VFS superblock accordingly

Therefore, the error handling code will need to execute shut_browsing (info) and kfree (info). Also, you need to match the kill called during umount_ block_ Super in previous real_ sfs_ bb. Kill defined in C_ The sb function is used with the function corresponding to the pointer.

The following are various code snippets:

#include <linux/slab.h> /* For kzalloc, ... */
...
static int sfs_fill_super(struct super_block *sb, void *data, int silent)
{
	sfs_info_t *info;

	if (!(info = (sfs_info_t *)(kzalloc(sizeof(sfs_info_t), GFP_KERNEL))))
		return -ENOMEM;
	info->vfs_sb = sb;
	if (init_browsing(info) < 0)
	{
		kfree(info);
		return -EIO;
	}
	/* Updating the VFS super_block */
	sb->s_magic = info->sb.type;
	sb->s_blocksize = info->sb.block_size;
	sb->s_blocksize_bits = get_bit_pos(info->sb.block_size);

	...
}

static void sfs_kill_sb(struct super_block *sb)
{
	sfs_info_t *info = (sfs_info_t *)(sb->s_fs_info);

	kill_block_super(sb);
	if (info)
	{
		shut_browsing(info);
		kfree(info);
	}
}

In order to complete the above operations and other actual SFS (Simula file system) operations, Pugs believes that it is necessary to take his own handle as a key parameter. He added this content to the following content (in the previous real_sfs_ds.h):

static int get_bit_pos(unsigned int val)
{
	int i;

	for (i = 0; val; i++)
	{
		val >>= 1;
	}
	return (i - 1);
}

And init_browsing() ,shut_browsing () is basically the function transformation of early user space_ real_ sfs. C to kernel space code real_sfs_ops.c. Prototype in real_sfs_ops.h. This basically involves the following transformations:

  • Convert "int sfs_handle" to "sfs_info_t * info"
  • lseek() and read() use sb_ Break() reads from the block device
  • Convert calloc () to vmalloc (), and then initialize appropriately with zero.
  • Convert free() to vfree()

The conversion here is init_browsing () and shutdown_ Browsing() in real_sfs_ops.c:

#include <linux/fs.h> /* For struct super_block */
#include <linux/errno.h> /* For error codes */
#include <linux/vmalloc.h> /* For vmalloc, ... */

#include "real_sfs_ds.h"
#include "real_sfs_ops.h"

int init_browsing(sfs_info_t *info)
{
	byte1_t *used_blocks;
	int i, j;
	sfs_file_entry_t fe;
	int retval;

	if ((retval = read_sb_from_real_sfs(info, &info->sb)) < 0)
	{
		return retval;
	}
	if (info->sb.type != SIMULA_FS_TYPE)
	{
		printk(KERN_ERR "Invalid SFS detected. Giving up.\n");
		return -EINVAL;
	}

	/* Mark used blocks */
	used_blocks = (byte1_t *)(vmalloc(info->sb.partition_size));
	if (!used_blocks)
	{
		return -ENOMEM;
	}
	for (i = 0; i < info->sb.data_block_start; i++)
	{
		used_blocks[i] = 1;
	}
	for (; i < info->sb.partition_size; i++)
	{
		used_blocks[i] = 0;
	}

	for (i = 0; i < info->sb.entry_count; i++)
	{
		if ((retval = read_from_real_sfs(info,
					info->sb.entry_table_block_start,
					i * sizeof(sfs_file_entry_t),
					&fe, sizeof(sfs_file_entry_t))) < 0)
		{
			vfree(used_blocks);
			return retval;
		}
		if (!fe.name[0]) continue;
		for (j = 0; j < SIMULA_FS_DATA_BLOCK_CNT; j++)
		{
			if (fe.blocks[j] == 0) break;
			used_blocks[fe.blocks[j]] = 1;
		}
	}

	info->used_blocks = used_blocks;
	info->vfs_sb->s_fs_info = info;
	return 0;
}
void shut_browsing(sfs_info_t *info)
{
	if (info->used_blocks)
		vfree(info->used_blocks);
}

Similarly, browse_ real_ sfs. All other functions in C must also be converted one by one. Also, note that the read from the underlying block device is controlled by two functions, read_sb_from_real_sfs() and read_from_real_sfs ()), which are encoded as follows:

#include <linux/buffer_head.h> /* struct buffer_head, sb_bread, ... */
#include <linux/string.h> /* For memcpy */

#include "real_sfs_ds.h"

static int read_sb_from_real_sfs(sfs_info_t *info, sfs_super_block_t *sb)
{
	struct buffer_head *bh;

	if (!(bh = sb_bread(info->vfs_sb, 0 /* Super block is the 0th block */)))
	{
		return -EIO;
	}
	memcpy(sb, bh->b_data, SIMULA_FS_BLOCK_SIZE);
	brelse(bh);
	return 0;
}
static int read_from_real_sfs(sfs_info_t *info, byte4_t block,
				byte4_t offset, void *buf, byte4_t len)
{
	byte4_t block_size = info->sb.block_size;
	byte4_t bd_block_size = info->vfs_sb->s_bdev->bd_block_size;
	byte4_t abs;
	struct buffer_head *bh;

	/*
	 * Translating the real SFS block numbering to underlying block device
	 * block numbering, for sb_bread()
	 */
	abs = block * block_size + offset;
	block = abs / bd_block_size;
	offset = abs % bd_block_size;
	if (offset + len > bd_block_size) // Should never happen
	{
		return -EINVAL;
	}
	if (!(bh = sb_bread(info->vfs_sb, block)))
	{
		return -EIO;
	}
	memcpy(buf, bh->b_data + offset, len);
	brelse(bh);
	return 0;
}

All of the above code snippets are treated as real_sfs_minimal.c (based on the previously created file real_sfs_bb.c), real_sfs_ops.c,real_sfs_ds.h (based on the same file created previously), real_sfs_ops.h and the supported Makefile together, as well as the previously created format_real_sfs.c application, available from rsfs_on_block_device_code.tbz2 get.

 

Real SFS on block device

Once you compile with make and get real_sfs_first.ko driver, Pugs will not expect it to be different from the previous real_ sfs_ bb. The KO driver is different, but at least for now it should be reading and validating the underlying partition. To do this, he first tries to install the regular partition of the pen drive to get the "invalid SFS detected" message in the dmesg output. Then format. Note that there is the same "Not a directory" error as in the previous article, but it still exists - it is still very similar to the previous bare metal driver anyway - core functions that have not been implemented - but blocking device partitions already exist. Figure 39 shows the exact commands for all these steps.

Note: the ". / format_real_sfs" and "mount" commands can take a lot of time (possibly in minutes), depending on the partition size. Therefore, it is best to use partitions less than 1MB.

Through the important step of interacting the file system module with the base block device, the last step of Pugs will be the execution from browser_real_sfs.c and use them in the SFS module accordingly.

 

Part III: running file system

This is the 24th article, part of the Linux device driver series, which makes the complete real SIMULA file system module work and has real hardware partitions on pen drives.

https://sysplay.in/blog/linux-device-drivers/2015/01/the-semester-project-part-vii-file-system-in-action/

In actual SFS operation

rsfs_ in_ action_ code. TBZ 2 Code provided already The final test implementation of the project in the last semester of Pugs & Shweta. It includes the following contents:

  • real_sfs.c – include early real_sfs_minimal.c code and other actual SIMULA file system functions. Note that the name of the file system changes from SFS to real_sfs
  • real_sfs_ops.c and real_sfs_ops.h – contains early code and enhanced real_sfs.c implement other operations required
  • real_sfs_ds.h (almost the same file as the previous article, and a spin lock is added to the real SFS information structure to prevent competition when accessing the used_blocks array in the same structure)
  • format_real_sfs.c (same as the file in the previous article) – the real SFS formatter
  • Makefile – includes using real_sfs _ *. * File build driver real_sfs_final.ko's rules and the use of format_real_sfs.c build format_ real_ Rules for SFS applications

Using all these and earlier details, Shweta completed their project documentation. Therefore, finally, Shweta & Pugs are ready for their last semester's project demonstration, demonstration and live broadcast.

The key points of the demonstration (on the root shell) are as follows:

  • Load real_sfs_final driver: insmod real_sfs_final.ko
  • Use the previously formatted pen drive partition / dev / sdb1 or use format_ real_ The SFS application reformats it:/ format_real_sfs / dev / sdb1. Warning: please review the full details of the previous article before actually formatting.
  • Mount the real SFS formatted partition: mount -t real_sfs / dev / sdb1 / mnt
  • Then... What is it? Browse the installation file system. Use the usual shell commands to operate on the file system: ls, cd, touch, vi, rm, chmod

Figure 40 shows the actual SIMULA file system

Figure 40: the actual SIMULA file system module is running

 

Implementation behind running

Moreover, if you really want to know what additional enhancements Pugs has made to the code in the previous chapter to make it reach this level, it is basically the following core system calls, which are part of the other four groups in the five groups of function pointer structure (in real_sfs.c):

  1. write_inode (under struct super_operations) – sfs_write_inode () basically obtains the pointer to the inode in the inode cache of VFS and is expected to synchronize it with the inode in the physical hardware space file system. This can be done by calling SFS with appropriate modifications_ Update () (defined in real_sfs_ops.c) (adapted from the previous browser_real_sfs application). The key parameter changes by passing the inode number instead of the file name and actual timestamp instead of the flag of its update status. Therefore, SFS based on file name_ The call to lookup () will be replaced with SFS based on inode number_ get_ file_ Entry() (defined in real_sfs_ops.c)), in addition, if the file size decreases, the data block will now be released (using sfs_put_data_block() (defined in real_sfs_ops.c)). Note that SFS_ put_ data_ Block () (defined in real_sfs_ops.c) is a reference to browser_ real_ Put in SFS application_ data_ Conversion of block(). Also note the interesting mapping / from VFS inode number to our zero based file entry index, using macro S2V_INODE_NUM() / V2S_INODE_NUM() in real_sfs_ops.h.
    Finally, use write_ to_ real_ SFS () implements the underlying write, and this function is added to real_sfs_ops.c, and read_ from_ real_ SFS () (already in real_sfs_ops.c), except that the direction of data transmission is reversed and the buffer is marked dirty to synchronize with the physical content. At the same time, in real_ sfs_ ops. In C, two wrapper functions read are written_ entry_ from_ real_ Sfs() (use read_from_real_sfs()) and write_entry_to_real_sfs() (use write_to_real_sfs()) to increase specific requirements for reading and writing file entries and code readability. sfs_write_inode () and sfs_update () is shown in the following code snippet. sfs_write_inode() written to file real_sfs.c. For other files, check the file real_sfs_ops.c
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,34))
static int sfs_write_inode(struct inode *inode, int do_sync)
#else
static int sfs_write_inode(struct inode *inode, struct writeback_control *wbc)
#endif
{
	sfs_info_t *info = (sfs_info_t *)(inode->i_sb->s_fs_info);
	int size, timestamp, perms;

	printk(KERN_INFO "sfs: sfs_write_inode (i_ino = %ld)\n", inode->i_ino);

	if (!(S_ISREG(inode->i_mode))) // Real SFS deals only with regular files
		return 0;

	size = i_size_read(inode);
	timestamp = inode->i_mtime.tv_sec > inode->i_ctime.tv_sec ?
			inode->i_mtime.tv_sec : inode->i_ctime.tv_sec;
	perms = 0;
	perms |= (inode->i_mode & (S_IRUSR | S_IRGRP | S_IROTH)) ? 4 : 0;
	perms |= (inode->i_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) ? 2 : 0;
	perms |= (inode->i_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) ? 1 : 0;

	printk(KERN_INFO "sfs: sfs_write_inode with %d bytes @ %d secs w/ %o\n",
		size, timestamp, perms);

	return sfs_update(info, inode->i_ino, &size, &timestamp, &perms);
}

int sfs_update(sfs_info_t *info, int vfs_ino, int *size, int *timestamp, int *perms)
{
	sfs_file_entry_t fe; 
	int i;
	int retval;

	if ((retval = sfs_get_file_entry(info, vfs_ino, &fe)) < 0) 
	{   
		return retval; 
	}   
	if (size) fe.size = *size;
	if (timestamp) fe.timestamp = *timestamp;
	if (perms && (*perms <= 07)) fe.perms = *perms;

	for (i = (fe.size + info->sb.block_size - 1) / info->sb.block_size;
		i < SIMULA_FS_DATA_BLOCK_CNT; i++)
	{   
		if (fe.blocks[i])
		{   
			sfs_put_data_block(info, fe.blocks[i]);
			fe.blocks[i] = 0;
		}   
	}   

	return write_entry_to_real_sfs(info, V2S_INODE_NUM(vfs_ino), &fe);
}
  1. Create, unlink, lookup (under struct inode_operations) – all three functions sfs_inode_create(),sfs_inode_unlink(),sfs_inode_lookup () has two general parameters (inode pointer of the parent and directory entry pointer of the file under consideration), which respectively create, delete and find the index node corresponding to the directory entry pointed to by its parameters (such as dentry).
    sfs_inode_lookup () basically uses SFS with appropriate modifications_ Lookup () (defined in real_sfs_ops.c) to search for the existence of the file name.) (adapted from the earlier browser_real_sfs application - the approach is to replace the user space lseek() + read() combination with read_entry_from_real_sfs()). If the filename is not found, call the generic kernel function d_splice_alias () creates a new inode entry in the underlying file system and attaches it to the directory entry pointed to by dentry. Otherwise, it simply caches additional inodes from the inodes of the VFS (using the general kernel function d_add()). If you just get this inode (I_NEW), you need to fill it with the actual SFS lookup file attribute. In all the above implementations and subsequent implementations, some basic assumptions have been made, namely:

     

    • All users in this mode are mapped to the real node, and the indexes of this mode are only mapped to the SFS for all other users
    • Real SFS only maintains one timestamp, which maps to all three creation, modification and access times of VFS index nodes.

    sfs_inode_create() and sfs_inode_unlink() calls the converted SFS respectively_ Create() and sfs_remove () (defined in real_sfs_ops.c) (adapted from the earlier browser_real_sfs application), creates and clears inode entries in the underlying hardware space file respectively. In addition to the usual inode cache operation, you can also use new_inode() + insert_inode_locked(),d_instantiate() and inode_dec_link_count (), instead of Iget, which I learned before_ Locked() and d_add(). In addition to permissions and file input parameters, replace lseek() + read() by combining read_entry_from_real_sfs() ,sfs_create () has an interesting transition from user space to kernel space: time (NULL) to get_seconds() . In sfs_create() and SFS_ In remove(), the user space lseek() + write() combination has been replaced by an obvious write_ entry_ to_ real_ Replaced by SFS (). As mentioned above, in the file real_sfs.c and real_ sfs_ ops. Check out all the above code segments in C.

  2. readpage,write_begin,writepage,write_end (under struct address_space_operations) – all these address space operations basically read and write blocks on the underlying file system and use the corresponding general kernel function mpage_readpage(),block_write_begin(),block_write_full_page()., generic_write_end(). The first one is in < Linux / mpage h> The other three are in < Linux / buffer_ head. h> Central Plains type.. Now, although these functions are general enough, if you think a little, you will find that the first three of them will eventually have to use the corresponding block layer API to conduct real SFS specific transactions with the underlying block devices (hardware partitions). And this is through the real SFS specific function sfs_get_block (), which has been passed and used by the first three functions mentioned above.
    Call sfs_get_block () (defined in real_sfs.c) to read the specific block number (iblock) of the file (represented by inode) into the buffer header (bh_result)), or you can choose to extract (allocate) a new block. Therefore, to do this, we will look up the block array of the corresponding actual SFS inode, and then use the kernel API map_bh() gets the corresponding block of the physical partition. To get a new block, we call SFS again_ get_data_block () (defined in real_sfs_ops.c), which is also for browser_real_sfs application get_ data_ Conversion of block(). Similarly, in the case of allocating new blocks, SFS is also used_ update_ file_ Entry() in real_ sfs_ ops. A linear implementation in C updates the real SFS inode below. The following code snippet shows sfs_get_block() implementation.
static int sfs_get_block(struct inode *inode, sector_t iblock,
				struct buffer_head *bh_result, int create)
{
	struct super_block *sb = inode->i_sb;
	sfs_info_t *info = (sfs_info_t *)(sb->s_fs_info);
	sfs_file_entry_t fe;
	sector_t phys;
	int retval;

	printk(KERN_INFO "sfs: sfs_get_block called for I: %ld, B: %llu, C: %d\n",
		inode->i_ino, (unsigned long long)(iblock), create);

	if (iblock >= SIMULA_FS_DATA_BLOCK_CNT)
	{
		return -ENOSPC;
	}
	if ((retval = sfs_get_file_entry(info, inode->i_ino, &fe)) < 0)
	{
		return retval;
	}
	if (!fe.blocks[iblock])
	{
		if (!create)
		{
			return -EIO;
		}
		else
		{
			if ((fe.blocks[iblock] = sfs_get_data_block(info)) ==
				INV_BLOCK)
			{
				return -ENOSPC;
			}
			if ((retval = sfs_update_file_entry(info, inode->i_ino, &fe))
				< 0) 
			{   
				return retval;
			}
		}
	}
	phys = fe.blocks[iblock];
	map_bh(bh_result, sb, phys);

	return 0;
}
  1. open,release,read,write,aio_read / read_iter (starting with kernel v3.16), aio_write / write_iter (starting from kernel v3.16), fsync (under struct file_operations of regular files) – all these operations should basically be cached by buffer, that is, they should be implemented using address space operations. This is a common requirement. The kernel provides a set of general kernel APIs, namely generic_file_open(),do_sync_read() / new_sync_read() (since kernel v3.16), do_sync_write() /new_sync_write() (since kernel v3.16), generic_file_aio_read() / generic_file_read_iter() (since kernel v3.16), generic_file_aio_write() / generic_file_write_iter() (since kernel v3.16), simple_sync_file () / noop_fsync() (kernel v2.6.35 as of). In addition, address space operations read and write are no longer required because the kernel v4 1 give. Please note that there is no API for publishing because it is a "return 0" API. Check out real_sfs.c file to get struct file_ operations sfs_ The exact definition of FOPS.
  2. Readdir / iterate (from kernel v3.11) (under struct file_operations in the directory) – sfs_readdir() / sfs_iterate() mainly reads the directory entries of the underlying directory (represented by files) and fills them into the VFS directory entry cache (pointed by the dirent parameter) (using the parameter function filldir) or enters the directory context (pointed by the ctx parameter) (since kernel v3.11).
    Since the actual SFS has only one directory and the top level has only one directory, this function basically uses the converted SFS_ List() fills the directory file cache with directory entries of all files in the base file system (defined in real_sfs_ops.c), adapted from browser_real_sfs application. Please check real_sfs.c file to get the complete sfs_readdir() / sfs_ The iterate () implementation, which starts with populating directory entries. "(current directory) and". " "The function using parameters (parent directory) filldir(), or the general kernel function dir_emit_dots() (because of kernel V3.11), and then use sfs_list() for all files of real SFS.
  3. put_ Super (lower structure super_operations) - previous custom implementation sfs_kill_sb () (directed to kill_sb) has been enhanced by separating it into custom parts and putting it into sfs_put_super () (now referred to as put_super), and the standard kill_ block_ The existence of super() is determined by kill_ Sb points directly to. Check out the file real_sfs.c make all these changes.

After all this is done, you can see the amazing demonstration by Pugs, as shown in Figure 40. Don't forget to use 'tail -f / var / log / to view the real-time log of / var / log / messages. Messages' to match each command you issue on the actual SFS file system that you have installed. This will give you the best idea of when to invoke which system call. Or, in other words, which application calls which system call from the front end of the file system. To track all system calls invoked by the application / command, use strace in the command, for example, type "strace ls" instead of "ls".

 

note

  1. In distributions like Ubuntu, you might find logs under / var / log / syslog instead of / var / log / messages

 

 

Tags: Operating System

Posted by jackie11 on Fri, 13 May 2022 17:34:41 +0300