Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Network File System (NFS) Implementation

A distributed network file system implemented in C with support for multiple clients, storage servers, and a centralized name server. This system provides file operations with access control, concurrent access handling, and persistence.

Architecture

Components

  1. Name Server (NM)

    • Central coordinator managing file locations and client connections
    • Implements efficient file search using Trie data structure
    • Maintains an LRU cache for frequently accessed metadata lookups, reducing repeated Trie traversals for hot paths.
    • Handles access control and user authentication
    • Routes client requests to appropriate storage servers
  2. Storage Server (SS)

    • Stores and manages file data
    • Supports concurrent read/write operations
    • Implements sentence-level locking for write operations
    • Provides undo functionality (one level)
    • Ensures data persistence
  3. Client

    • User interface for file operations
    • Supports all required operations (VIEW, READ, WRITE, etc.)
    • Direct connection to storage servers for data transfer
    • Interactive command-line interface

Features

File Operations

  • VIEW - List files with optional flags (-a, -l)
  • READ - Read complete file contents
  • CREATE - Create new empty files
  • WRITE - Word-level editing with sentence locking
  • UNDO - Revert last change to a file
  • INFO - Display file metadata and access control
  • DELETE - Remove files (owner only)
  • STREAM - Word-by-word streaming with 0.1s delay
  • LIST - List all registered users
  • EXEC - Execute file contents as shell commands
  • CHECKPOINT - Create snapshot of storage server state

Access Control

  • ADDACCESS - Grant read (-R) or write (-W) access
  • REMACCESS - Remove user access
  • Owner always has read/write access
  • Write access implies read access

Advanced Features

  • ✅ Efficient file search using Trie (O(m) where m = filename length)
  • ✅ LRU-based metadata caching for frequently accessed file lookups
  • ✅ Sentence-level locking for concurrent writes
  • ✅ Data persistence across server restarts
  • ✅ Comprehensive logging to files and console
  • ✅ Error handling with standardized error codes
  • ✅ Dynamic storage server registration

Compilation

Prerequisites

  • GCC compiler
  • POSIX-compliant system (Linux/Unix)
  • pthread library

Build All Components

make

Build Individual Components

make naming_server   # Build name server
make storage_server  # Build storage server
make client          # Build client

Clean Build Artifacts

make clean      # Remove binaries and object files
make distclean  # Remove everything including storage

Usage

1. Start the Name Server

./bin/naming_server

The name server listens on port 8080 by default.

2. Start Storage Server(s)

Important: Each storage server is uniquely identified by its IP address and client port combination. The system automatically detects the server's IP and creates a unique storage directory (e.g., ./storage_192_168_1_10_9001/).

./bin/storage_server <nm_ip> <nm_port> <client_port>

Examples:

On localhost (same machine, different ports):

./bin/storage_server 127.0.0.1 8080 9001  # Creates ./storage_127_0_0_1_9001/
./bin/storage_server 127.0.0.1 8080 9002  # Creates ./storage_127_0_0_1_9002/

Across multiple devices (different machines on network):

# On Device 1 (192.168.1.10):
./bin/storage_server 192.168.1.5 8080 9001  # Creates ./storage_192_168_1_10_9001/

# On Device 2 (192.168.1.11):
./bin/storage_server 192.168.1.5 8080 9001  # Creates ./storage_192_168_1_11_9001/

The storage server will:

  • Auto-detect its own IP address
  • Create a unique storage directory based on IP:Port
  • Register with the naming server at the specified IP
  • List all existing files in its storage directory
  • Listen for client connections on the specified port

3. Start Client(s)

./bin/client <nm_ip> <nm_port>

Examples:

# Connect to local naming server:
./bin/client 127.0.0.1 8080

# Connect to remote naming server:
./bin/client 192.168.1.5 8080

Example:

./client 127.0.0.1 8080

You will be prompted for a username, then enter the interactive command shell.

Command Reference

VIEW - List Files

VIEW        # List files you have access to
VIEW -a     # List all files in system
VIEW -l     # List files with details
VIEW -al    # List all files with details

READ - Read File

READ <filename>

CREATE - Create File

CREATE <filename>

WRITE - Edit File

WRITE <filename> <sentence_number>
# Then enter word updates:
<word_index> <content>
<word_index> <content>
...
ETIRW  # Finish writing

Example:

WRITE test.txt 0
Client: 1 Hello world
Client: 3 beautiful
Client: ETIRW

UNDO - Revert Changes

UNDO <filename>

INFO - File Information

INFO <filename>

DELETE - Remove File

DELETE <filename>  # Must be owner

STREAM - Stream File Content

STREAM <filename>  # Displays word-by-word with 0.1s delay

LIST - Show Users

LIST

ADDACCESS - Grant Access

ADDACCESS -R <filename> <username>  # Read access
ADDACCESS -W <filename> <username>  # Write access

REMACCESS - Remove Access

REMACCESS <filename> <username>

EXEC - Execute File

EXEC <filename>  # Executes content as shell commands

CHECKPOINT - Create Snapshot

CHECKPOINT  # Creates a checkpoint of all storage servers

Creates a timestamped snapshot of all registered storage servers' data and metadata. Checkpoint files are stored in ./checkpoints/ directory with naming format: checkpoint_<ip>_<port>_<YYYYmmddHHMMSS>.tar

On storage server restart, the most recent checkpoint is automatically restored if available.

Implementation Details

Efficient Search

The Name Server maintains a Trie-based namespace index that enables O(m) file lookup, where m is the pathname length.

To reduce repeated metadata traversals, the system additionally maintains an LRU (Least Recently Used) cache for recently resolved paths. Frequently accessed files are served directly from the cache, while infrequently accessed entries are automatically evicted using the LRU replacement policy.

This hybrid design combines deterministic Trie lookups with temporal locality, improving lookup performance under realistic workloads while maintaining bounded memory usage.

Concurrent Access

  • Multiple clients can read the same file simultaneously
  • Write operations lock individual sentences
  • Other sentences in the same file remain accessible
  • Locks are automatically released after write completion

Sentence Parsing

  • Sentences are delimited by ., !, or ?
  • Every delimiter creates a new sentence (including "e.g." → multiple sentences)
  • Words are separated by spaces
  • WRITE operations can insert new delimiters, creating new sentences

Data Persistence

  • Storage servers maintain files in ./storage/ directory
  • Undo history stored as .undo files
  • File metadata tracked in name server
  • Access control lists persist with name server

Checkpointing

Checkpointing provides a way for storage servers to create a durable snapshot of their current on-disk state and lightweight metadata so that a server restart or recovery can restore to a known good point. Checkpoints are designed to be compact, consistent at the storage-server level, and quick to write so they can be performed during maintenance windows or on-demand.

  • What a checkpoint contains:

    • A compact archive of the storage server's file data and directory layout (files under the server's storage directory).
    • Metadata required to restore file-level information (undo files, small metadata files) and any server-local state needed by that storage server.
    • A timestamp and the storage server identifier (IP:Port) used to locate/identify the checkpoint file.
  • Where checkpoints are stored:

    • Checkpoints are written to a checkpoints/ directory inside the workspace (e.g. ./checkpoints/).
    • Files are named using the server identifier and timestamp: checkpoint_<ip>_<port>_<YYYYmmddHHMMSS>.tar (dots in the IP are converted to underscores).
  • Triggering checkpoints:

    • Admins can trigger a checkpoint from the naming server (if supported) or by sending an explicit checkpoint request to a storage server. The exact mechanism depends on your deployment; the common approaches are:
      • A naming-server-level CHECKPOINT control command that instructs all registered storage servers to create a checkpoint.
      • An admin script that connects to a storage server's control port and requests a checkpoint.
  • Restore behavior:

    • On startup, a storage server will look for the most recent checkpoint file for its identifier in ./checkpoints/ and, if found, attempt to restore files and metadata from it before processing client requests.
    • If a restore fails or no checkpoint is found, the server will continue with files present on disk in its storage directory.
  • Notes & limitations:

    • Checkpointing is scoped to a single storage server. There is no global, coordinated snapshot across multiple storage servers; achieving a consistent global snapshot requires quiescing clients or using an external orchestration procedure.
    • Creating a checkpoint can be I/O intensive on large datasets. Schedule checkpoints during low-traffic windows or when the system is quiesced.
    • Checkpoint/restore logic is intended for operational convenience and faster recovery; it is not a substitute for external backups or replication.
    • When using checkpoints in production, verify permissions and available disk space for the checkpoints/ directory.

Logging

Each component logs to:

  • Console (stdout) - Important messages
  • Log files - Detailed operation logs
    • NM.log - Name server logs
    • SS.log - Storage server logs

Log format: [timestamp] [level] message

Error Handling

Comprehensive error codes:

  • ERR_FILE_NOT_FOUND - File doesn't exist
  • ERR_ACCESS_DENIED - Insufficient permissions
  • ERR_SENTENCE_LOCKED - Sentence being edited by another user
  • ERR_INDEX_OUT_OF_RANGE - Invalid sentence/word index
  • ERR_SERVER_UNAVAILABLE - Storage server unreachable
  • And more...

Architecture Decisions

Communication Protocol

  • TCP sockets for reliable communication
  • Custom message protocol with serialization
  • Length-prefixed messages to handle variable-length data

File Registry

  • Name Server maintains a Trie-based namespace index.
  • Maps pathname → metadata → storage server.
  • Frequently accessed metadata is cached using an LRU cache.
  • Cache misses fall back to Trie traversal.
  • Trie lookup complexity: O(m), where m is the pathname length.

Metadata Caching

  • The Name Server employs an LRU (Least Recently Used) cache to store recently resolved file metadata.
  • The cache exploits temporal locality exhibited by filesystem workloads, where recently accessed files are more likely to be accessed again.

On every metadata lookup:

  1. Check the LRU cache.
  2. On a cache hit, return the cached metadata immediately.
  3. On a cache miss, perform Trie traversal.
  4. Insert the resolved metadata into the cache.
  5. Evict the least recently used entry when the cache reaches capacity.

This design improves average lookup latency while keeping memory usage bounded.

Access Control

  • Owner-based permissions (first user in ACL)
  • Hierarchical access: WRITE implies READ
  • Stored in memory at name server

Undo Mechanism

  • One-level undo per file
  • Previous version saved before each write
  • File-level undo (not user-specific)

Testing

Basic Operations Test

# Terminal 1: Start name server
./naming_server

# Terminal 2: Start storage server
./storage_server 127.0.0.1 8080 9001

# Terminal 3: Client 1
./client 127.0.0.1 8080
# Username: user1
nfs> CREATE test.txt
nfs> WRITE test.txt 0
Client: 1 Hello world
Client: ETIRW
nfs> READ test.txt

# Terminal 4: Client 2
./client 127.0.0.1 8080
# Username: user2
nfs> READ test.txt  # Should fail (no access)
nfs> LIST           # Should show user1, user2

Concurrent Access Test

# Client 1
nfs> WRITE test.txt 0  # Lock sentence 0

# Client 2 (while Client 1 is writing)
nfs> WRITE test.txt 1  # Should succeed (different sentence)
nfs> WRITE test.txt 0  # Should fail (sentence locked)

Access Control Test

# Client 1 (owner)
nfs> CREATE myfile.txt
nfs> ADDACCESS -R myfile.txt user2
nfs> INFO myfile.txt

# Client 2
nfs> READ myfile.txt   # Should succeed
nfs> WRITE myfile.txt 0 # Should fail (read-only)

Known Limitations

  1. Single name server (no redundancy)
  2. One-level undo only
  3. No file versioning
  4. In-memory access control (lost on restart)
  5. No encryption for data transfer
  6. Limited to text files only
  7. LRU cache is maintained locally within the Name Server and is not replicated.

Future Enhancements

  • Name server replication for fault tolerance
  • Multi-level undo/redo
  • File versioning and history
  • Persistent access control lists
  • SSL/TLS encryption
  • Binary file support
  • File compression
  • Distributed metadata cache shared across replicated Name Servers
  • Load balancing across storage servers

Troubleshooting

"Failed to connect to Name Server"

  • Ensure name server is running
  • Check IP and port are correct
  • Verify no firewall blocking connections

"File not found"

  • File may not exist - use VIEW -a to list all files
  • Check spelling of filename
  • Ensure storage server containing file is running

"Access denied"

  • You don't have permissions for this file
  • Ask owner to grant access via ADDACCESS
  • Use INFO to check current access list

"Sentence is locked"

  • Another user is currently editing this sentence
  • Wait for them to finish (ETIRW)
  • Edit a different sentence

"Connection lost during stream"

  • Storage server crashed or disconnected
  • Restart storage server and try again

References

License

This project is an academic implementation for educational purposes.

Authors

Developed as part of Operating Systems and Networks course project

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages