VFS: How d you guys handle mountpoints?

Discussions on more advanced topics such as monolithic vs micro-kernels, transactional memory models, and paging vs segmentation should go here. Use this forum to expand and improve the wiki!
Post Reply
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

VFS: How d you guys handle mountpoints?

Post by mariuszp »

Glidix currently has a VFS with all the typical UNIX features (except symlinks, which are to be implemented soon). However, I'm starting to think that my implementation is not the best.

The main problem with it is that it may not necessarily be the most efficient, and this stems mainly from how it stores mountpoints. When you mount a filesystem, it is assigned a "prefix"; for example, if I might my CD-ROM at /media/cdrom, the kernel will add the following structure to a linked list:

Code: Select all

typedef struct _MountPoint
{
	char			prefix[512];
	FileSystem		*fs;
	struct _MountPoint	*prev;
	struct _MountPoint	*next;
} MountPoint;
Where "prefix" will be "/media/cdrom/" (i.e. all paths starting with "/media/cdrom/" are on this filesystem). When trying to resolve the path "/media/cdrom/my_files/hello.txt", it will see that it begins with "/media/cdrom/" and therefore successfully detect that it is on that filesystem; then, the rest of the path, "my_files/hello.txt" is followed, by calling fs->openroot(), which returns a "Dir" structure, and the system will repeadetly call dir->next() until "my_files" is found, then it will call dir->opendir() which returns another Dir, and eventually it finds the file (if it exists of course).

But this means that the only way processes can have working directories is if they just store their paths, and that path must be strcat()ed with every relative path used, and a lot of pointless directory-traversing will then happen... but otherwise, of course, it wouldn't know which filesystem to go on, if a subdirectory of the working directory is a mountpoint.

How does your OS store mountpoints? I can think of another way: to just store the device (st_dev) and inode (st_inode) of each mountpoint, and redirect all operations on such inodes to the root of the filesystem. But the problem would be that /initrd, /dev, etc must be mounted before the root filesystem, but they are on the root filesystem. I could mount some kind of "ramfs" on the root, then unmount everything and mount the proper filesystems.

But before doing this I wanted to see how other OSes do this. I can't find any information on how linux stores this either.
AbstractYouShudNow
Member
Member
Posts: 92
Joined: Tue Aug 14, 2012 8:51 am

Re: VFS: How d you guys handle mountpoints?

Post by AbstractYouShudNow »

Hi,

Lookup the JamesM tutorial on filesystems, it has a quite elegant way of doing that AFAIK
mariuszp
Member
Member
Posts: 587
Joined: Sat Oct 16, 2010 3:38 pm

Re: VFS: How d you guys handle mountpoints?

Post by mariuszp »

JamesM's tutorial marks some inodes with a flag (FS_MOUNTPOINT), this is clearly not a good idea; either you'd have to save that flag to disk and each filesystem driver would have to support it, or you'd have to have a list of mounted dev-inode pairs anyway.
User avatar
max
Member
Member
Posts: 616
Joined: Mon Mar 05, 2012 11:23 am
Libera.chat IRC: maxdev
Location: Germany
Contact:

Re: VFS: How d you guys handle mountpoints?

Post by max »

In Ghost, a node contains
- type
- virtual id
- physical fs id
- name
- delegate
- parent
- list of children

When the type of the node is "mountpoint", the node additionally has a "delegate" set. Whenever anyone tries to open a file, list a directory, read from a file etc. the delegate is used.

For example, a process wants to open "/examplefs/documents/test.txt":
1. find the node "examplefs" by searching the list of children in the root; if its not found, ask the root's delegate to discover the node
2. find the node "documents" by searching the list of children in the found node; if its not found, the delegate for "examplefs" is asked to discover the node
3. find the node "test.txt" ... using the delegate of "examplefs" as before

"Discovering" here means, searching on the actual physical filesystem/whatever to search for a specific node.
You probably also want to have processes to be filesystem drivers: for this, there is the "task delegate". Whenever this delegate is asked to do anything, it sends a message to the process that registered itself to be the driver for that mountpoint, sets the requesting task asleep, and waits until the action is finished. The filesystem driver process receives this message, does whatever is necessary to do the requested action (like using system calls to create the virtual filesystem nodes etc.) and then tells the kernel that the transaction is finished. The requesting process is the woken up and can continue as if nothing happened.

The root node is just a node with no name, that has a special delegate (the root delegate) that just lists all mounpoints created in root.

Thats how I do it. In short, mounpoints have a delegate that have an implementation to access a specific filesystem.
User avatar
KemyLand
Member
Member
Posts: 213
Joined: Mon Jun 16, 2014 5:33 pm
Location: Costa Rica

Re: VFS: How d you guys handle mountpoints?

Post by KemyLand »

I'll share my VFS's design on what mountpoints respect.

First of all, some important data structures and functions (Spoiler, C++11 is involved!):

Code: Select all

namespace VFS {
	enum class InodeType {
		File,
		Directory,
		SymbolicLink,
		ProtocolFile, // i.e: socket
		NamedPipe,
		TrapFile, // i.e: "special" files
		Gateway // Let's leave this for another forum thread...
	};
	
	struct Inode {
		Size		id; // As far as the underlying filesystem knows
		UID		owner;
		Size		refs; // (Hard-links)
		Size		openRefs; // (Open file descriptors)
		UInt16	access;
		Size		size;
		InodeType	type;
		Bool		mayBeUncached;
	};
	
	class Directory;
	
	struct File {
		Byte		name[256];
		Size		mount;
		Size		inode;
		Directory*	parent;
		File*		next;
		InodeType	type;
	};
	
	class Directory : public File {
		public:
			File*	children;
			
			ErrorCode	getChild	(String name, File **file);
	};
	
	ErrorCode	Mount	(String source, String target, Bool bindMount);
	ErrorCode	Unmount	(String root);
}
Some BTWs:
  • typedef unsigned long Size; // For x86
  • class ErrorCode; // ...
  • typedef bool Bool; // This *has* its reasons
  • typedef const char* String; // Did you though it was something like std::string?
The job of VFS::Mount() is very simple:

If bindMount is true, then source shall be a path pointing to a valid, present directory. That directory and its children shall be copied into a new, fresh tree. The address of that new directory structure is stored in a example pointer named sourceDir. The bind-mounted directory structure itself may not be copied, it's innecessary.

Else, the trap file pointed to by source is loaded into kernel memory and its internal filesystem is parsed into the common tree structure in kernel memory. The directory representation of the root of the loaded filesystem is stored in the previously mentioned sourceDir pointer.

Whatever *sourceDir contains, the target directory, target, shall be empty for everything to work correctly. There will be a single change in the original filesystem tree, that is, all first-level nodes from the root (i.e: /usr counts but /usr/local doesn't) shall have their parent changed to the directory pointed to by target, as well as target shall change its childs pointer member changed to the address of the first element of the mounted filesystem.

The reverse process, VFS::Unmount() is even simpler. It just flushes all data in the mounted filesystem, removes it from kernel memory, and sets the host root's childs pointer member to nullptr.

This model is simple, yet it addresses bind mounting, nested mounts, and multiple mounts of a single filesystem. BTW, obviously, there is a hidden mountpoint list that avoids VFS::Unmount()'ing of non-mountpoints, and there are several minimal, but important details that I didn't said here, specially with the "multiple mounts of a single filesystem" feature.
Happy New Code!
Hello World in Brainfuck :D:

Code: Select all

++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
[/size]
Post Reply