Chapter 15: File-System Internals: Mounting, Virtual File Systems, and NFS

0:00 / 0:00
Report an issue

Welcome to Last Minute Lecture.

This free chapter overview is designed to help students review and understand key concepts.

These summaries supplement, not replace, the original textbook and may not be redistributed or resold.

For complete coverage, always consult the official text.

Okay, let's unpack this.

Have you ever stopped to think about what's really happening when you save a file, open an app, or even just boot up your computer?

There's this entire hidden world behind your screen, like a silent conductor orchestrating all your digital data.

Yeah, absolutely.

Today, we're taking a deep dive into file system internals, pulling insights from a leading textbook on operating systems to uncover the crucial mechanisms that manage your digital life.

And what's truly fascinating here is how operating systems ingeniously store, organize, and access information.

So our mission for you today is to explore everything from the foundational concepts to how files are shared across the globe, uncovering why these systems are designed the way they are and maybe what that means for how you interact with your devices every single day.

Exactly.

Because when you look at your computer, it's not just a handful of files, right?

We're talking thousands, maybe millions.

Oh, easily billions on other systems.

Billions.

And they all live on what we call random access storage devices, you know, hard drives, SSDs, optical disks, that sort of thing.

But how do operating systems keep track of all that?

Well, think of your computer's storage like a massive office building.

The entire building, that's your storage device, your hard drive or SSD.

Okay.

Big building.

Now, that building isn't just one huge open space.

It's divided into several floors or distinct sections.

Those are your partitions.

Ah, partitions.

I've seen that term.

Right.

And each partition might then contain different suites or offices, which we call volumes.

Okay.

So device, partition, volume.

Got it.

And then within each of those volumes, you have a very specific,

structured way of organizing all the documents and supplies that's your file system.

Filing cabinet system for that office suite.

Exactly.

That conceptual hierarchy, kind of like figure 15 .1 in the text describes, is how operating systems manage these immense amounts of data efficiently.

Without it, it'd just be chaos.

And here's something that might surprise you.

Maybe.

It's rarely just one type of file system running on your computer.

Your operating system actually juggles, well, a surprising variety.

Like looking at Solaris in figure 15 .2, there's a whole list.

Absolutely.

While you might think of the file system like NTFS or APFS, the truth is operating systems deploy a whole arsenal of specialized ones, each one designed for a unique purpose.

Like what?

What kind of special purposes?

Okay.

So you might have TFs, which is a temporarily file system.

It lives only in volatile main memory, your RAM, and poof, it's gone when you reboot.

Great for, you know, temporary scratch data.

Okay, fast but temporary.

Right.

Or OBJFs, which is a virtual file system.

It lets debuggers look at kernel symbols like they were files, super useful for developers.

Oh, clever.

Then there are things like CTFs for managing process contracts, LOFS, which is a loopback system letting you access one file system in a different location.

Like an alias.

Sort of, yeah.

And PROCIFS, another virtual one, presents information about running processes as files.

You can literally CD into a process ID and Ls its threads or open file descriptors.

Whoa, okay.

That's neat.

And then you have your general purpose workhorses like SUFs or ZFs.

So this raises an important question.

Why so many types?

It seems complicated.

Because different tasks and different hardware require specialized ways of managing data effectively.

A temporary RAM disk has very different needs than a massive permanent storage array.

It's all about optimization.

Right.

Choosing the right tool for the job.

Okay, so if all these different file systems and volumes exist, how do they actually become

usable on your computer?

Like, how do I get to the files inside them?

Ah, this next step, file system mounting, is surprisingly fundamental, just like you open a file to use it.

You have to mount a file system.

Exactly.

You have to mount it to make it available.

It's conceptually pretty straightforward, actually.

You tell the operating system two main things.

The device name you want to connect, like devdisk2 is one.

Okay, the raw device.

And an empty folder, what we call a mount point in your existing file system structure.

Maybe volumes my USB stick or emmit data.

So like sticking a label on an empty spot on the shelf.

Kind of, yeah.

The OS then checks if the device actually contains a valid file system it understands.

Some OS ICs need you to tell them the type, others can figure it out.

If it's all good, the OS basically graphs that new volume onto your existing directory tree at the mount point.

So if you look at figure 15 .3, you see an unmounted volume, just device -esque.

Then figure 15 .4 shows it mounted over users, suddenly the files inside device -esque appear under users.

Ah, I see.

So it integrates it seamlessly.

Exactly.

Imagine you have a separate hard drive with old home directories, let's say the volume is devsdb1.

You could mount it at home.

So Jane's directory, which was originally just Jane on that separate drive, now becomes home Jane in your main system view.

Or you could mount it as users, making her directory users Jane.

Precisely.

It's all about integrating that separate storage area into your unified directory tree.

And different operating systems handle this a bit differently, right?

Like, what if that user's directory already had stuff in it?

Good question.

Some OSs won't let you mount over a directory that already has files.

Others will, but they'll just hide the original contents until you unmount the new volume.

Some even let you mount the same file system in multiple places.

Others don't.

It varies.

Interesting quirks.

How about different OS families?

Well, Make OS, for example, usually automatically finds and mounts file systems when you plug them in, typically under the volumes directory, giving them nice folder icons.

Super user -friendly.

Yeah, I've seen that.

Microsoft Windows traditionally used drive letters, you know, C, D, F, etc., though newer versions are more flexible and can also do UNIX -style mounting anywhere in the tree.

Windows also tries to automatically discover and mount all file systems it finds when it boots up.

And UNIX?

UNIX systems traditionally often require explicit mount commands in the terminal, though you can set up a configuration file, like etc .fstab, to have it mount specific things automatically at boot time.

OK.

Speaking of booting up, how do all these file systems and storage devices figure into that?

You mentioned partitions before.

Disks aren't usually just one big block, right?

They're sliced up.

That's right.

Your physical disk drive is often sliced into multiple partitions.

Think of it like dividing that office building floor into separate office suites.

OK.

And you said these partitions can be raw or cooked.

Sounds like a kitchen.

Huh.

Yeah, a bit.

A raw partition means it has no traditional file system formatted onto it.

It's just raw blocks.

Why would you want that?

For specialized purposes.

Like, UNIX often uses a raw partition for its SwapSpace virtual memory backing.

Some high -performance databases manage their own storage directly on raw partitions for speed.

Or RAID systems might store metadata, like bitmaps tracking mirrored blocks, on a raw slice.

I see.

So special cases.

Exactly.

A cooked partition, then, is the normal case it's formatted with a file system ready to store your files and directories.

So what does this all mean for actually starting your computer, the boot process?

Crucially, for your computer to even start, one of these partitions, usually the one holding the operating system, needs boot information.

This is typically a small sequential series of blocks right at the beginning of the partition, loaded as an image.

It's the Bootstrap Loader.

The first program that runs.

Right.

And this little loader program is just smart enough to understand the file system on that partition, find the main operating system kernel file, and load that into memory.

This raises an important question.

What about dual booting?

Like having Windows and Linux on the same machine?

Good point.

The bootloader itself can be more sophisticated.

A dual bootloader, like GRUB, understands multiple file systems and knows how to find and load kernels for different operating systems stored on different partitions.

It gives you that menu at the start.

Oh, okay.

So the loader handles the choice.

Exactly.

The main partition containing the OS kernel, often called the root partition, is always mounted automatically at boot.

Other volumes, like your data partitions or external drives, can be mounted automatically based on configuration or manually later.

And every time the OS mounts a volume, it does a quick check to make sure the file system looks valid if it finds inconsistencies.

Uh -oh.

Yeah.

It usually means it needs a consistency check, like FLAC on UNAX or 2KDSC on Windows before it'll mount it properly.

Right.

Got to make sure the filing cabinet isn't a total mess.

Moving beyond just individual machines, file sharing is huge, right?

For collaboration, efficiency, especially in multi -user systems.

Oh, absolutely.

It's fundamental to network computing.

But as soon as you start sharing files between multiple users, you run into some core challenges.

Like what?

Well, naming consistency is one, how do you refer to the same file from different machines?

Then there's access control, who gets to read this file, who gets to write to it?

And tied to that is protection.

How do you enforce those controls?

So how do systems implement this?

How do they manage who owns what?

It often comes down to the concepts of file owner and file group.

Owner and group.

Okay.

The owner is typically the user who created the file or directory.

They usually have the most control.

They can change attributes like permissions and grant or revoke access to others.

They're the boss of that file.

Pretty much.

A group is just a named set of users.

You can assign a group to a file and then set permissions specifically for members of that group.

It allows controlled sharing among a team, for example.

So owner gets full rights, group gets some rights, and maybe everyone else gets even fewer rights.

That's the classic UNIX model.

Exactly.

You have three sets of permissions.

One for the owner, one for the group, and one for others, everyone else.

The owner defines what each of those sets can do, read, write, execute.

Makes sense.

But what's fascinating here, and a real practical challenge sometimes, is when you an external hard drive between different computers, especially different UNIX -like systems.

Because permissions are usually based on numeric user IDs, UIDs, and group IDs, GIDs.

User Jane might be UID 1001 on your machine, but UID 1005 on my machine.

If you bring a disk formatted on your machine over to mine, the system just sees UID 1001 owns the file.

It doesn't know that was your Jane.

It might correspond to a different user or no user on my system.

Oh, wow.

So you could accidentally give someone else access or lose access yourself.

You often have to take steps to manually match up UIDs or reset ownership when moving disks between systems that don't share a centralized user management system.

It's a common source of concussion.

Yeah, I can see that.

Okay, so modern operating systems support multiple file system types, maybe even running simultaneously.

How do they blend all that together?

How can I drag a file from my main hard drive, maybe NTFS, to a USB stick, maybe FAT30?

And have it just work without me even noticing the difference?

Ah, that feels like magic, doesn't it?

But there's a really brilliant piece of engineering behind it.

The solution is virtual file systems, often abbreviated as VFS.

Virtual file systems, okay.

Think of it as using object -oriented techniques to create this amazing modularity.

If you imagine the layers, like in figure 15 .5, at the very top, you have the standard interface that applications use.

Your standard file system calls open, read, write, close.

Things programmers are familiar with.

Write the API?

Exactly.

Then, underneath that, you have the VFS layer.

This is where the magic happens.

It acts as a universal translator.

How so?

It separates those generic operations, like read block number five, from the specific implementation of how a particular file system type, like NTFS, XT4, APFS, or even a network file system like NFS, actually does that reading.

So it provides a common front end.

Precisely.

And crucially, the VFS introduces a mechanism for uniquely representing a file or directory while it's actively being used by the system, even across a network.

This is often called a V -node.

A V -node?

How's that different from an inode?

Good question.

An inode number is typically unique only within a single, specific file system volume.

A V -node is a structure the kernel maintains in GEMRI for every active file or directory.

And it contains information allowing it to be uniquely identified, potentially even across multiple machines in a network.

It often holds a pointer to the underlying inode for local files, but it's a more general concept.

Okay, so a V -node is the VFS's internal tag for an open file or directory.

You got it.

And then below the VFS layer, you have the actual code specific to each file system type or the procedures for remote protocols like NFS.

The VFS layer knows which specific function to call down there based on what kind of file system the bone node represents.

This sounds really powerful, especially for systems like Linux that embrace variety.

Oh, definitely.

Linux's VFS architecture is a great example.

It uses four main internal object types.

The inode object representing an individual file's metadata.

The file object representing an open file instance.

Multiple processes can open the same inode.

Each gets a file object.

The superblock object representing an entire mounted file system.

And the dentry object representing a directory entry, linking a name to an inode.

Lots of objects.

Yeah, but the key is that each of these object types has a table of associated functions like the inode object has functions for create, lookup, link, etc.

The file object has read, write, close.

Function pointers, basically.

Exactly.

So when the VFS layer needs to perform an operation, say read from an open file, it looks at the file object, finds its function table, and calls the read function from that specific table.

Ah.

So the VFS itself doesn't need to know the nitty gritty details of exta4 versus XFS for

Precisely.

This raises an important point.

The VFS doesn't know or really care whether a particular inode object represents a local disk file, a directory, or even a file on a remote server.

It just knows how to find the right function table and call the appropriate function, like read or lookup, for whatever object it's dealing with.

It's a beautiful abstraction.

That is clever.

Okay, let's step beyond just the local machine then.

How do we handle files when they're not on our computer?

Sharing costs, networks, you mentioned NFS, but how did we get here?

It's been quite an evolution.

I mean, originally sharing meant manual transfers, you'd use FTP or something similar to explicitly copy files back and forth.

Tedious.

Yeah, been there.

Then get the idea of distributed file systems, DFS, where remote directories could be made visible locally, just like we discussed with mounting.

Suddenly, server share docs might appear right there in your file browser.

Much more seamless.

That was a big leap.

Huge.

Then, interestingly, the World Wide Web kind of took us back towards manual transfers, at least initially clicking download links in a browser.

But now, increasingly, cloud computing platforms like Dropbox, Google Drive, OneDrive, they dominate remote file access and collaboration for many people.

Right, the cloud is everywhere.

But behind many of these distributed systems, there's a common pattern, isn't there?

Yes, the client -server model is fundamental to most remote file systems.

You have one or more server machines that actually store the files.

They hold the data.

And then you have client machines that need to access that data.

The server exports or shares certain resources, maybe entire volumes, maybe specific directories, and specifies which clients are allowed to connect.

And a machine could be both, right?

Serving some files, accessing others.

Absolutely.

A workstation might serve its local project directory to team members, but also mount a central shared library from a dedicated file server.

So what does this all mean for security?

If my machine is asking a server for a file,

how does the server know it's really my machine and, more importantly, me asking?

That's a critical and often tricky part.

Client identification is a challenge.

Relying just on the client's network name or IP address isn't very secure because those can potentially be spoofed -faked.

More secure solutions involve things like encrypted keys or authentication protocols like Kerberos.

But setting those up can add complexity and compatibility issues.

So honestly, many common methods are less secure than ideal.

Like what?

How does something like NFS handle it typically?

Well, in classic UNAIX NFS, especially older versions, authentication often relies heavily on the client's networking information, like its IP address and the user ID, UID, being presented in the request.

The UID again?

Yeah.

The server essentially trusts the client machine to correctly report the UID of the user making the request.

This means that for permissions to work as expected, the user IDs on the client machine and the server machine must match.

If Jane is UID 1001 on the client, she must also be UID 1001 on the server.

And if they don't match.

Then access rights might be determined incorrectly.

If UID 1001 on the client maps to, say,

Bob, 1801008, on the server, the server might grant access based on Bob's permissions, not Jane's.

The server trusts the client's claim about the user ID.

Oof.

Okay.

That highlights the need for consistent user management across machines.

Definitely.

Which leads us to a bigger picture.

If we connect all these networked machines, how do we manage all this shared information?

Not just files, but user accounts, passwords, group memberships, machine names, network services.

Right.

You can't manage user lists manually on hundreds of machines.

Exactly.

That's where Distributed Information Systems, sometimes called Distributed Naming Services, come into play.

They aim to provide unified, consistent access to this kind of information across a network.

What are some examples?

Well, the biggest one, the one the whole internet relies on, is DNS, the Domain Name System.

It handles translating human -readable host names, like www .example .com, into network IP addresses.

It replaced older, unscalable methods like sharing a single host's file everywhere.

Okay.

DNS for names.

Yeah.

What about users and passwords?

Historically, in the UNIX world, Sun Microsystems developed NIS, the Network Information Service.

It was originally called Yellow Pages, or YP, but trademarks.

NIS centralized things like usernames, passwords, unfortunately often sent insecurely in clear text, and host names across a network.

It made administration easier, but had security weaknesses.

Clear text passwords.

Yikes.

Yeah.

They tried to fix it with NIS plus C, which was much more secure, but also significantly more complex, so it never got the same widespread adoption.

So what did Microsoft do?

Microsoft developed CIFS, the Common Internet File System, an evolution of SMB, which handles file sharing, often using network information combined with user authentication, username and password.

But again, it often relies on usernames matching across machines for seamless access.

Their bigger solution, though, is Active Directory.

Ah, Active Directory.

I've heard of that in enterprise settings.

Active Directory provides a unified, hierarchical namespace for pretty much everything in a Windows network, users, groups, computers, printers, policies.

It uses Microsoft's implementation of the Kerberos Network Authentication Protocol for security.

So it's a central directory for the whole network.

Essentially, yes.

And the emerging trend, the industry standard for this kind of thing now,

is LDP, the Lightweight Directory Access Protocol.

It's a secure, standardized way to access and maintain distributed directory information.

In fact, Active Directory itself is largely based on LDA concepts.

Many organizations use LDP for central user authentication,

address books, finding resources like printers.

Imagine the potential.

A single secure LDA directory could potentially store all user and resource information for an entire organization.

This enables things like secure a single sign -on, log in once, access everything you're allowed to, and dramatically simplify system administration.

No more managing user accounts on dozens of individual servers.

That sounds much better.

But with all these interconnected systems, VFS, NFS, LDAP, things must break sometimes, right?

What happens when they do?

Oh, absolutely.

Failure is a fact of life in complex systems, and how they fail differs between local and remote setups.

Yeah, so?

Well, local file system failures are usually pretty concrete.

Your hard drive might physically fail.

The directory structure, the metadata could get corrupted.

Maybe the disk controller or the cable goes bad, or simple user admin error accidentally deleting the wrong thing.

Right.

And that usually results in?

Often, a system crash or an inability to access data requiring human intervention, maybe replacing hardware, restoring from backup, running recovery tools.

Okay.

What about remote file system failures?

Those are even more complex because now you've added the network dependency.

The network itself could fail, hardware issues, misconfiguration.

The server machine could crash, or maybe it's just down for scheduled maintenance.

So more points of failure.

Exactly.

And this leads to tricky questions about how systems should respond when a remote resource suddenly becomes unavailable.

Yeah, what do they do if I'm saving a file to a network drive and the network cuts out?

This gets into failure semantics.

What should the system do?

One option is to just terminate the operation immediately, fail fast.

That sounds bad.

Potential data loss, user gets an error.

Yep.

It can lead to data loss and definitely user frustration.

So most distributed file system protocols, like NFS, choose a different path.

They delay operations.

Wait it out.

Essentially, yes.

They pause the operation, maybe retrying periodically, hoping the server will become available again relatively soon.

This feels better to the user as their application might just hang for a bit instead of crashing, but it relies on the server eventually coming back online.

And this ties back to that state information you mentioned.

It does.

Recovering seamlessly often requires knowing the state of operations, which brings us back to NFS.

NFS version 3, the widely deployed one, famously implements a stateless DFS.

Stateless.

What does that mean again?

It means the server doesn't keep track of which clients have files open or what operations are performing between requests.

Each request from a client must contain all the information needed to perform it, like the file handle, offset, data.

Why do that?

Robustness.

If the server crashes and reboots, it doesn't need to reconstruct any complex client state.

It just starts answering requests again.

Clients might need to resend requests that didn't get a reply, but the server itself has no recovery burden related to client state.

But you said that makes it unsecure.

That's the trade off.

Yeah.

Because it's stateless, it tends to trust incoming requests more readily.

It's harder to implement strong authentication or guarantees without state.

For example, it might be possible to forge requests more easily.

So NFS version 4 tried to fix that.

Exactly.

NFS version 4 was introduced as a stateful version specifically to improve security, add features like file locking, which is hard without state, and potentially improve performance in some scenarios.

But v3 remains very common due to its simplicity and long history.

OK, one more critical aspect when multiple people are sharing files.

How do their actions affect each other in real time?

If I save a change, when does my colleague see it?

Ah, you're talking about consistency semantics.

These are the rules that define exactly how simultaneous access to the same shared file behaves,

particularly regarding the visibility of modifications made by one user to others.

Is it like process synchronization, semaphores and stuff?

It's related conceptually, but the challenges are different, mainly because of the much higher latencies involved with disk I .O.

and especially network communication.

You can't just use simple locks quite as effectively.

We often talk about a file session, the series of accesses a single process makes to a file between opening it open and closing it close.

OK, so what are the common models?

Let's look at three prominent examples.

First, the classic UNIX semantics.

How does that work?

Pretty intuitively, actually.

When one user writes to an open file, those changes are essentially visible immediately to any other user who also happens to have that same file open, or as close to instantly as the system can manage caching and writebacks.

Also, importantly, users can share in the same current file position pointer.

If I read 10 bytes, advancing the pointer, and then you read 10 bytes, you'll get the next 10 bytes after mine.

So everyone sees the exact same single image of the file evolving in real time.

Exactly.

The file has a single image that reflects an inner leaving of all accesses, regardless of who made them.

The downside is this can lead to performance bottlenecks or delays if many users are trying to write to the same file concurrently.

OK, what's another model?

Session semantics.

This is famously used by the Andrew file system or OpenFS.

It behaves quite differently.

How so?

With session semantics, when you write to an open file, those changes are not immediately visible to other users who currently have it open.

Wait, really?

So where do the changes go?

They're typically cached locally or only affect your current session.

The changes made by one user only become visible to other users in new sessions that start after the modifying user has closed the file.

So I save and close, then you open it and you see my changes.

But if we both have it open, we don't see each other's live edits.

That's the gist of it.

What's fascinating here is that, conceptually, a file can temporarily be associated with several possibly different images, at the same time the original version seen by one and the modified but not yet closed version seen by another.

This allows more concurrency for reads and writes, as users aren't directly interfering with each other's view until a close operation makes the changes permanent and visible for subsequent opens.

Different trade -offs.

Performance versus immediate consistency.

Precisely.

And the third model is immutable shared file semantics.

Immutable.

Meaning unchanging.

Exactly.

Once a file is created and declared as shared by its creator, it simply cannot be modified.

Ever.

Its name can't be reused for a different file, and its contents cannot be altered.

So you can only create new files, never change existing shared ones.

Right.

If you need an updated version, you create a new file with the updates.

Wow.

That sounds restrictive, but I guess it simplifies things.

Tremendously.

Think about implementing this in a distributed system.

Since shared files are guaranteed to be read only, caching becomes trivial.

You never need to worry about cache invalidation.

Sharing is disciplined and simple.

It avoids all the complex consistency issues.

But maybe not practical for all use cases, like collaborative document editing.

Definitely not.

But for things like software distribution or shared libraries, it can be very effective.

Okay, let's circle back to NFS, the network file system.

You said it's a cornerstone.

Can we deep dive into how it works as a real -world example?

Maybe thinking about figures 15 .6 and 15 .7, which show these multi -machine setups.

Sure.

NFS is really important.

It's both an implementation and a specification, originally from Sun Microsystems, but now an open standard part of ONC plus Coload.

It's supported by pretty much all UNIX -like systems and many others, including Windows.

It usually runs over TCP or UDP IP.

And as we mentioned, version 3 is still the most commonly deployed, though version 4 adds significant features.

And its goal is transparency, right?

Making remote files look local.

Exactly.

NFS aims to let you treat a network of interconnected workstations as mostly independent machines, but allow transparent sharing on request.

A machine can be both a client accessing remote files and a server sharing its local files, as shown conceptually in figure 15 .6.

So how does that sharing actually get set up?

The mount operation.

Right.

The core mechanism is mounting a directory from a remote server over a directory on the local client machine.

Once mounted, the remote directory looks like just another subtree within the client's local file system.

Can you walk through figure 15 .7a?

Okay, so imagine you have machineU, your client, and machineS1, a server.

ServerS1 decides to share its local directory as shared.

On your machineU, you choose an empty local directory, say uslocal, as the mount point.

You then issue a mount command telling you to mount s1 .us shared onto uslocal.

Now on machineU, if you navigate to uslocal, you're actually seeing the contents of us shared from serverS1.

If there's a subdirectory dir1 inside us shared on s1, you access it on you via the path us or local dir1.

It feels completely local.

Seamless integration.

What about figure 15 .7b?

That looks more complex.

That shows cascading mounts.

Let's say you've already mounted s1 .usr shared onto usrlocal like before.

Now suppose another server, s2, shares its directory usrdir2.

You could potentially mount s2 .usrdir2 over the directory us or local usr1, a directory that itself came from the first remote mount.

Whoa.

Mounting a remote directory over another remote directory.

Exactly.

This allows for really flexible configurations and can support user mobility.

Maybe your home directory itself is mounted from one server and project files within it are mounted from another.

And a key design goal of NFS was to work across different types of machines, operating systems, and networks, a heterogeneous environment.

How did they achieve that?

By using standardized protocols built on RPC, remote procedure called primitives.

RPC allows a process on one machine to call a function or procedure on another machine as if it were local.

NFS uses RPCs defined on top of XDR, the external data representation protocol, which handles differences in data formats between machines, like byte order.

Okay, so RPC and XDR handle the communication.

You mentioned NFS has two main protocols.

That's right.

First is the mount protocol.

This is used just to establish that initial logical connection between client and server for a specific shared directory.

How does the server know what it's allowed to share?

The server maintains an export list.

On Solaris, it's typically xrd ssdf dstab.

This file lists the local file systems or directories that the server is willing to share, and importantly, which clients are permitted to mount them, often specified by hostname or IP address.

So the client asks to mount something.

Right.

The client sends a mount request using the mount protocol RPC, specifying the directory it wants.

The server checks its export list.

If the client is permitted, the server returns a file handle.

File handle.

What's that?

It's basically a unique key or token that the client will use for all subsequent accesses to files within that mounted file system.

In UNIX terms, it typically contains information like a file system identifier and an inode number that uniquely identifies the root of the exported directory on the server.

It's opaque to the client.

The client just passes it back to the server in future requests.

Okay, so the mount gives the client the magic key.

Pretty much.

The server also typically keeps a list of which clients currently have its directories mounted, mainly for administrative purposes, like if the server needs to shut down, it might try to notify clients with active mounts.

But this list is just a hint.

It's not essential for correctness, which helps preserve that statelessness we talked about.

Right.

Okay, so that's mounting.

What's the second protocol?

That's the main NFS protocol itself.

This handles all the actual remote file access operations after a successful mount.

It provides a set of RPCs for things like looking up a filename in a directory, reading directory entries, creating or deleting files, reading or writing file data, accessing file attributes like size and permissions, managing links, and so on.

Ah, so all the normal file operations.

But notice anything interesting here.

The list of RPCs you just gave seems to omit standard open and close operations.

That seems intentional.

You nailed it.

That omission is absolutely central to NFS version 3's design.

It directly reflects the core feature we discussed, NFS servers are stateless.

Because open and close imply maintaining state about who has the file open.

Exactly.

Since the server doesn't maintain information about clients between accesses, there's no concept of a persistent open file state on the server side.

Each request, read, write, lookup must be self -contained.

It has to provide the file handle, identifying the file system and inode, and any other necessary arguments like the absolute byte offset for a read or write operation.

And this statelessness is what makes it robust to crashes.

That's the major benefit, yes.

No special measures are needed to recover server state after a crash related to client activity.

It just starts responding to the self -contained requests again.

For this to work reliably though, operations generally need to be idempotent.

Idempotent.

Meaning performing the same operation multiple times has the same effect as performing it just once.

Think about a network glitch causing a client to resend a delete file request.

If the operation isn't idempotent, the second request might cause an error because the file is already gone.

NFS uses techniques like sequence numbers within requests to help achieve idempotence for non -idempotent file operations.

Okay.

And you mentioned synchronous writes before.

Right.

Another consequence of statelessness and the need for robustness is that when a client performs a write operation, the NFS protocol, typically for version 3, requires that the modified data must be committed to the server's stable storage, usually the disk, before the server sends the results back to the client.

So the client waits until it's really saved on the server disk.

Yes.

This ensures that if the server crashes immediately after acknowledging the write, the data isn't lost.

But it comes at a cost, a potential performance penalty, as disk writes are slow.

This is often mitigated these days by servers having non -volatile, battery -backed caches on their disk controllers, which can acknowledge writes very quickly while still guaranteeing safety.

Makes sense.

But NFS doesn't provide concurrency control.

Correct.

The NFS protocol itself does not include mechanisms for locking files or coordinating simultaneous writes from multiple clients to the same file.

If two clients write to the same remote file concurrently, their writes might get intermixed unpredictably.

Users or applications are expected to coordinate access outside of NFS, perhaps using advisory locking mechanisms if the OS supports them over NFS, or simply by social convention.

Okay.

So how does this all fit together in the OS?

If I, on a client machine, do a read system call in a file I know is remote, what's the path?

Phaser 15 .8 seems to show this flow.

Right.

Let's trace it.

Your application makes a standard read system call.

The client OS kernel takes over.

The VFS layer on the client determines that the file descriptor corresponds to a VNode representing an NFS file.

Okay.

VFS knows it's remote.

So instead of calling a local file system function, the client's VFS calls into the NFS client code.

This code packages the read request, including the file handle, offset, length, into an NFS RPC message.

Sends it over the network.

Sends it via RPC over the network to the NFS server daemon running on the server machine.

On the server side, the NFS daemon receives the RPC request, unpacks it, and essentially passes it up to the server's own VFS layer.

Ah, the server uses VFS too.

Yes.

That's the beauty.

The server's VFS treats this incoming request much like a local system call.

It uses the file handle to find the correct local inode, performs the requested operation, for example, reads the data from its local disk via its own file system specific drivers, and gets the result.

And sends it back.

The result, the data read, or a status code, is passed back down through the server's NFS layers, packaged into an RPC reply, sent back across the network to the client's NFS code, passed up through the client's VFS, and finally returned to the original application process as the result of its read system call.

The key is that both client and server machines are architecturally identical internally regarding VFS.

They just play different roles in the RPC exchange.

Wow.

That's quite a round trip, but the VFS makes it manageable.

What about path names?

If I do is srlocaldayonefile .txt, and srlocal is an NFS mount point.

Path name translation is another interesting aspect.

Because the NFS server is stateless, it doesn't know the client's full path or mount structure.

So translating a path name like that typically involves breaking it down into components.

USR, local, dir1, file .txt.

The client's NFS code has to handle this.

It starts with the file handle for the mounted directory, that's our local in this case, and sends an NFS lookup RPC to the server for the next component, dir1.

The server finds dir1 within the directory corresponding to the initial file handle, and returns its file handle.

The client then uses that new file handle to send another lookup request for the next component, file .txt.

So each slash in the path potentially means another round trip to the server?

Potentially yes.

This sounds like it could be quite inefficient, right?

Yeah, very.

Especially for long paths.

It can be, and it's a necessary consequence of the stateless design.

The server only knows how to look up one component at a time within a directory specified by a file handle.

It's unaware of the client's overall mount structure or path context.

So how do they make it bearable?

It must be caching.

Exactly.

To optimize this, clients maintain a directory name lookup cache, DNLC.

They cache the results of recent lookup operations mapping a directory of node and a component name to the resulting child of mode file handle.

This cache can significantly speed up references to files that share the same initial path components, avoiding many of those RPC round trips.

Ah, that makes sense.

Any catches?

There's a slight caveat with those cascading mounts we mentioned earlier.

If a server itself has mounted another file system within an exported directory, a client might sometimes see the underlying directory on the server rather than the one the server itself has mounted there.

Due to the way lookups work component by component, it's a bit of an edge case.

What about caching file data and attributes?

NFS relies heavily on client -side caching for performance.

The client maintains caches for file attributes.

Information like file size, modification time, permissions, essentially the inode information.

File blocks,

actual data blocks read from files.

So if I read the same block twice, the second time it comes from the client cache.

Ideally, yes, but the client needs to be careful.

It only uses cached data if it believes its cached attributes are reasonably up to date.

It typically checks with the server when a file is open and then assumes the cached attributes are valid for a certain period, for example, maybe 60 seconds by default, before revalidating.

Clients also use read -ahead, fetching blocks before they're requested,

and delayed write, buffering writes locally before sending them to the server techniques.

However, that delayed write has a significant consequence.

Which is?

It means that strict UNIX semantics where writes are immediately visible to others are not preserved by NFS.

If you write to a file on an NFS client, those changes might sit in the client's cache for some time, maybe 30 seconds, before being flushed to the server.

Another client accessing the same file during that window won't see your changes yet.

So NFS doesn't guarantee immediate visibility.

Exactly.

This raises an important point.

So NFS provides neither strict UNIX semantics nor the session semantics of AFS.

It's kind of its own thing, prioritizing robustness and performance over strict consistency in some cases.

So despite these consistency drawbacks?

Despite those drawbacks, its simplicity, especially v3, robustness, vendor neutrality, and generally good performance, have made NFS arguably the most widely used multi -vendor distributed file system in the world.

It just works well enough for most common use cases.

Wow.

What a journey.

We've really gone from the ground up understanding the basic anatomy of a file system, that whole device partition volume structure.

Through the intricate process of mounting, making those volumes actually appear in your directory tree.

And sharing files, dealing with owners, groups, and those tricky UID, GID issues.

Then diving into the magic of virtual file systems, that VFS layer acting as the universal translator.

Right.

The VNodes and function tables.

Yeah.

All the way to the complex world of remote access with NFS, client server models,

statelessness versus statefulness.

And finally, those different consistency semantics, UNIX, session, immutable, and realizing NFS doesn't perfectly fit any of them.

It's really a testament to the ingenuity that goes into designing these foundational technologies that we often just take for granted every day.

And understanding these internals, even at a conceptual level, is so crucial.

Whether you're developing software that interacts with files, managing networks with shared storage, or even just trying to figure out why your system is behaving a certain way.

Knowing why helps.

Exactly.

Knowing why things are designed the way they are helps you diagnose problems, make better choices, and ultimately build or use systems more effectively.

So what does this all mean for you, our listener?

Well, it means that even the simplest act, like saving a document or opening a photo, kicks off this incredibly complex symphony of hidden operations,

all designed ultimately to make your digital life feel seamless and organized.

Yeah.

And maybe it leaves you with a final thought to chew on.

If you were designing the next generation of file sharing systems today, knowing everything we've just discussed, how would you navigate those fundamental tradeoffs, especially between raw performance and strict data consistency?

How would you balance them, given the absolutely massive scale of today's cloud platforms and the demands of billions of mobile devices all wanting access?

There's no single easy answer.

Definitely something to think about.

Well, that's all the time we have for today.

Thanks for joining us on the Deep Drive.

ⓘ This audio and summary are simplified educational interpretations and are not a substitute for the original text.

Chapter SummaryWhat this audio overview covers
File-system internals encompass the architectural foundations that enable operating systems to organize, manage, and provide unified access to files stored on local and remote devices. Understanding file-system structures begins with recognizing how partitions and volumes segment storage devices and how mounting mechanisms integrate disparate file systems into a cohesive namespace accessible to users and applications. Boot procedures, raw partitions, and multi-boot loaders establish the groundwork for system initialization and file-system discovery. Metadata management—including file ownership, permissions, and access controls—becomes essential in multi-user environments where security and resource isolation must be enforced. File sharing extends beyond single machines into distributed contexts, requiring robust authentication schemes, user and group identification, and sophisticated access control mechanisms. The Virtual File System layer serves as an abstraction that unifies multiple file-system implementations, allowing the operating system to interact with diverse storage technologies through a consistent interface. Linux exemplifies this design through standardized objects such as inodes, file structures, superblocks, and dentry entries that translate abstract file operations into concrete device I/O. Remote file systems introduce additional complexity through client-server architectures where distributed naming services—including DNS, NIS, and LDAP—resolve file locations across networks. Network security relies on protocols such as CIFS and NFS alongside authentication frameworks like Active Directory. Consistency semantics define how file modifications propagate across distributed systems, with UNIX, session, and immutable-shared-files models offering different trade-offs between coherence and performance. The Network File System protocol demonstrates these principles through its mount procedures, stateless server design, and RPC/XDR communication mechanisms. Path-name translation, cascading mounts, caching strategies, failure modes, and recovery techniques illustrate the operational complexities of maintaining reliable networked file access under realistic conditions where latency, disconnection, and server failures must be anticipated and mitigated.

Using this chapter to study? Last Minute Lecture is free and student-run. If it helped, consider supporting the project.

Support LML ♥