pojntfx / go-nbd

Pure Go NBD server and client library.
Apache License 2.0
346 stars 18 forks source link
golang nbd nbd-client nbd-server
Project icon

go-nbd

Pure Go NBD server and client library.


hydrun CI Go Version Go Reference Matrix

Overview

go-nbd is a lean NBD server and client library supporting the baseline protocol.

It enables you to:

Installation

You can add go-nbd to your Go project by running the following:

$ go get github.com/pojntfx/go-nbd/...@latest

Tutorial

TL;DR: Define a backend, expose it with a server, connect a block device with the client and setup/mount the filesystem.

1. Define a Backend

First, define a backend; it should conform to this simple interface:

type Backend interface {
    ReadAt(p []byte, off int64) (n int, err error)
    WriteAt(p []byte, off int64) (n int, err error)
    Size() (int64, error)
    Sync() error
}

A simple file-based backend could look like this:

// server/main.go

type FileBackend struct {
    file *os.File
    lock sync.RWMutex
}

func NewFileBackend(file *os.File) *FileBackend {
    return &FileBackend{file, sync.RWMutex{}}
}

func (b *FileBackend) ReadAt(p []byte, off int64) (n int, err error) {
    b.lock.RLock()

    n, err = b.file.ReadAt(p, off)

    b.lock.RUnlock()

    return
}

func (b *FileBackend) WriteAt(p []byte, off int64) (n int, err error) {
    b.lock.Lock()

    n, err = b.file.WriteAt(p, off)

    b.lock.Unlock()

    return
}

func (b *FileBackend) Size() (int64, error) {
    stat, err := b.file.Stat()
    if err != nil {
        return -1, err
    }

    return stat.Size(), nil
}

func (b *FileBackend) Sync() error {
    return b.file.Sync()
}

See pkg/backend for more backend examples.

2. Expose the Backend With a Server

Next, create the backend and expose it with a server:

// server/main.go

b := NewFileBackend(f)

for {
    conn, err := l.Accept()
    if err != nil {
        continue
    }

    go func() {
        if err := server.Handle(
            conn,
            []server.Export{
                {
                    Name:        *name,
                    Description: *description,
                    Backend:     b,
                },
            },
            &server.Options{
                ReadOnly:           *readOnly,
                MinimumBlockSize:   uint32(*minimumBlockSize),
                PreferredBlockSize: uint32(*preferredBlockSize),
                MaximumBlockSize:   uint32(*maximumBlockSize),
            }); err != nil {
            panic(err)
        }
    }()
}

See cmd/go-nbd-example-server-file/main.go for the full example.

3. Connect to the Server with a Client

In a new main package, connect to the server by creating a client; note that you'll have to modprobe nbd and run the command as root:

// client/main.go

if err := client.Connect(conn, f, &client.Options{
    ExportName: *name,
    BlockSize:  uint32(*blockSize),
}); err != nil {
    panic(err)
}

See cmd/go-nbd-example-client/main.go for the full example.

4. Setup and Mount the Filesystem

Lastly, create a filesystem on the block device and mount it:

$ sudo mkfs.ext4 /dev/nbd0
$ sudo mkdir -p /mnt
$ sudo mount -t ext4 /dev/nbd0 /mnt

You should now be able to use the mounted filesystem by navigating to /mnt.

🚀 That's it! We can't wait to see what you're going to build with go-nbd.

Examples

To make getting started with go-nbd easier, take a look at the following examples:

Acknowledgements

Contributing

To contribute, please use the GitHub flow and follow our Code of Conduct.

To build and start a development version of one of the examples locally, run the following:

$ git clone https://github.com/pojntfx/go-nbd.git
$ cd go-nbd
$ mkdir -p out && rm -f out/disk.img && truncate -s 10G out/disk.img && go run ./cmd/go-nbd-example-server-file --file out/disk.img
$ go run ./cmd/go-nbd-example-server-memory

# With the C NBD client
$ sudo umount ~/Downloads/mnt; sudo nbd-client -d /dev/nbd1 && echo 'NBD starting' | sudo tee /dev/kmsg && sudo nbd-client -N default localhost 10809 /dev/nbd1

# With the Go NBD client
$ sudo umount ~/Downloads/mnt; go build -o /tmp/go-nbd-example-client ./cmd/go-nbd-example-client/ && sudo /tmp/go-nbd-example-client --file /dev/nbd1

$ sudo mkfs.ext4 /dev/nbd1
$ sync -f ~/Downloads/mnt; sudo umount ~/Downloads/mnt; sudo rm -rf ~/Downloads/mnt && sudo mkdir -p ~/Downloads/mnt && sudo mount -t ext4 /dev/nbd1 ~/Downloads/mnt && sudo chown -R "${USER}" ~/Downloads/mnt

Have any questions or need help? Chat with us on Matrix!

License

go-nbd (c) 2024 Felicitas Pojtinger and contributors

SPDX-License-Identifier: Apache-2.0