skeleton for an fmap driver (IMAGE.BE). Start of an Network Block Device driver. NBD server python script I foundon the net for testing.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20963 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
François Revol
2007-05-02 00:38:45 +00:00
parent 8c5d3c422a
commit 4d1cc41eb3
7 changed files with 492 additions and 0 deletions
@@ -0,0 +1,4 @@
SubDir HAIKU_TOP src add-ons kernel drivers disk virtual ;
#SubInclude HAIKU_TOP src add-ons kernel drivers disk virtual fmap ;
SubInclude HAIKU_TOP src add-ons kernel drivers disk virtual nbd ;
@@ -0,0 +1,13 @@
SubDir HAIKU_TOP src add-ons kernel drivers disk virtual fmap ;
SetSubDirSupportedPlatformsBeOSCompatible ;
if $(TARGET_PLATFORM) != haiku {
# Needed for <ACPI.h>. Unfortunately we also get the other headers there,
# that we don't really want.
UsePublicHeaders drivers ;
}
KernelAddon fmap : kernel drivers bin :
fmap.c
;
@@ -0,0 +1,60 @@
/*
* Copyright 2006, François Revol. All rights reserved.
* Distributed under the terms of the MIT License.
*/
/*
fmap driver for Haiku
Maps BEOS/IMAGE.BE files as virtual partitions.
*/
#include <KernelExport.h>
#include <Drivers.h>
#include <Errors.h>
#define MAX_FMAPS 4
#define DEVNAME_FMT "disk/virtual/fmap/%2d"
status_t
init_hardware (void)
{
return B_OK;
}
status_t
init_driver (void)
{
return B_OK;
}
void
uninit_driver (void)
{
}
static const char *fmap_name[MAX_FMAPS+1] = {
NULL
};
device_hooks fmap_hooks = {
NULL, /* open */
NULL, /* close */
NULL, /* free */
NULL, /* control */
NULL, /* read */
NULL, /* write */
NULL, NULL, NULL, NULL
};
const char**
publish_devices()
{
return fmap_name;
}
device_hooks*
find_device(const char* name)
{
return &fmap_hooks;
}
@@ -0,0 +1,15 @@
SubDir HAIKU_TOP src add-ons kernel drivers disk virtual nbd ;
SetSubDirSupportedPlatformsBeOSCompatible ;
#if $(TARGET_PLATFORM) != haiku {
# # Needed for <ACPI.h>. Unfortunately we also get the other headers there,
# # that we don't really want.
# UsePublicHeaders drivers ;
#}
UsePrivateHeaders drivers ;
KernelAddon nbd :
nbd.c
;
@@ -0,0 +1,136 @@
#!/usr/bin/python
# from http://lists.canonical.org/pipermail/kragen-hacks/2004-May/000397.html
import struct, socket, sys
# network block device server, substitute for nbd-server. Probably slower.
# But it works! And it's probably a lot easier to improve the
# performance of this Python version than of the C version. This
# Python version is 14% of the size and perhaps 20% of the features of
# the C version. Hmm, that's not so great after all...
# Working:
# - nbd protocol
# - read/write serving up files
# - error handling
# - file size detection
# - in theory, large file support... not really
# - so_reuseaddr
# - nonforking
# Missing:
# - reporting errors to client (in particular writing and reading past end)
# - multiple clients (this probably requires copy-on-write or read-only)
# - copy on write
# - read-only
# - permission tracking
# - idle timeouts
# - running from inetd
# - filename substitution
# - partial file exports
# - exports of large files (bigger than 1/4 of RAM)
# - manual exportsize specification
# - so_keepalive
# - that "split an export file into multiple files" thing that sticks the .0
# on the end of your filename
# - backgrounding
# - daemonizing
class Error(Exception): pass
class buffsock:
"Buffered socket wrapper; always returns the amount of data you want."
def __init__(self, sock): self.sock = sock
def recv(self, nbytes):
rv = ''
while len(rv) < nbytes:
more = self.sock.recv(nbytes - len(rv))
if more == '': raise Error(nbytes)
rv += more
return rv
def send(self, astring): self.sock.send(astring)
def close(self): self.sock.close()
class debugsock:
"Debugging socket wrapper."
def __init__(self, sock): self.sock = sock
def recv(self, nbytes):
print "recv(%d) =" % nbytes,
rv = self.sock.recv(nbytes)
print `rv`
return rv
def send(self, astring):
print "send(%r) =" % astring,
rv = self.sock.send(astring)
print `rv`
return rv
def close(self):
print "close()"
self.sock.close()
def negotiation(exportsize):
"Returns initial NBD negotiation sequence for exportsize in bytes."
return ('NBDMAGIC' + '\x00\x00\x42\x02\x81\x86\x12\x53' +
struct.pack('>Q', exportsize) + '\0' * 128);
def nbd_reply(error=0, handle=1, data=''):
"Construct an NBD reply."
assert type(handle) is type('') and len(handle) == 8
return ('\x67\x44\x66\x98' + struct.pack('>L', error) + handle + data)
# possible request types
read_request = 0
write_request = 1
disconnect_request = 2
class nbd_request:
"Decodes an NBD request off the TCP socket."
def __init__(self, conn):
conn = buffsock(conn)
template = '>LL8sQL'
header = conn.recv(struct.calcsize(template))
(self.magic, self.type, self.handle, self.offset,
self.len) = struct.unpack(template, header)
if self.magic != 0x25609513: raise Error(self.magic)
if self.type == write_request:
self.data = conn.recv(self.len)
assert len(self.data) == self.len
def reply(self, error, data=''):
return nbd_reply(error=error, handle=self.handle, data=data)
def range(self):
return slice(self.offset, self.offset + self.len)
def serveclient(asock, afile):
"Serves a single client until it exits."
afile.seek(0)
abuf = list(afile.read())
asock.send(negotiation(len(abuf)))
while 1:
req = nbd_request(asock)
if req.type == read_request:
asock.send(req.reply(error=0,
data=''.join(abuf[req.range()])))
elif req.type == write_request:
abuf[req.range()] = req.data
afile.seek(req.offset)
afile.write(req.data)
afile.flush()
asock.send(req.reply(error=0))
elif req.type == disconnect_request:
asock.close()
return
def mainloop(listensock, afile):
"Serves clients forever."
while 1:
(sock, addr) = listensock.accept()
print "got conn on", addr
serveclient(sock, afile)
def main(argv):
"Given a port and a filename, serves up the file."
afile = file(argv[2], 'rb+')
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', int(argv[1])))
sock.listen(5)
mainloop(sock, afile)
if __name__ == '__main__': main(sys.argv)
@@ -0,0 +1,218 @@
/*
* Copyright 2006-2007, François Revol. All rights reserved.
* Distributed under the terms of the MIT License.
*/
/*
nbd driver for Haiku
Maps BEOS/IMAGE.BE files as virtual partitions.
*/
#include <KernelExport.h>
#include <Drivers.h>
#include <Errors.h>
#include <ksocket.h>
#include "nbd.h"
#define MAX_NBDS 4
#define DEVICE_PREFIX "disk/virtual/nbd/"
#define DEVICE_FMT DEVICE_PREFIX "%2d/raw"
#define DEVICE_NAME_MAX 32
#define MAX_REQ_SIZE (32*1024*1024)
struct nbd_request_entry {
struct nbd_request_entry *next;
struct nbd_request req;
bool r; /* is read */
size_t len;
void *buffer; /* write: ptr to passed buffer; read: ptr to malloc()ed extra */
};
struct nbd_device {
//lock
vint32 refcnt;
uint64 req; /* next ID for requests */
int sock;
thread_id postoffice;
uint64 size;
struct nbd_request_entry *reqs;
};
typedef struct cookie {
struct nbd_device *dev;
} cookie_t;
/* data=NULL on read */
status_t nbd_alloc_request(struct nbd_device, struct nbd_request_entry **req, size_t len, const char *data);
status_t nbd_post_request(struct nbd_device, uint64 handle, struct nbd_request_entry **req);
status_t nbd_dequeue_request(struct nbd_device, uint64 handle, struct nbd_request_entry **req);
status_t nbd_free_request(struct nbd_device, struct nbd_request_entry *req);
#pragma mark ==== request manager ====
#pragma mark ==== nbd handler ====
int32 postoffice(void *arg)
{
struct nbd_device *dev = (struct nbd_device *)arg;
int sock = dev->sock;
return 0;
}
#pragma mark ==== device hooks ====
status_t nbd_open(const char *name, uint32 flags, cookie_t **cookie) {
(void)name; (void)flags;
*cookie = (void*)malloc(sizeof(cookie_t));
if (*cookie == NULL) {
dprintf("nbd_open : error allocating cookie\n");
goto err0;
}
memset(*cookie, 0, sizeof(cookie_t));
return B_OK;
err0:
return B_ERROR;
}
status_t nbd_close(void *cookie) {
(void)cookie;
return B_OK;
}
status_t nbd_free(cookie_t *cookie) {
free(cookie);
return B_OK;
}
status_t nbd_control(cookie_t *cookie, uint32 op, void *data, size_t len) {
switch (op) {
case B_GET_DEVICE_SIZE: /* this one is broken anyway... */
if (data) {
*(size_t *)data = (size_t)cookie->dev->size;
return B_OK;
}
return EINVAL;
case B_SET_DEVICE_SIZE: /* broken */
return EINVAL;
case B_SET_NONBLOCKING_IO:
return EINVAL;
case B_SET_BLOCKING_IO:
return B_OK;
case B_GET_READ_STATUS:
case B_GET_WRITE_STATUS:
if (data) {
*(bool *)data = false;
return B_OK;
}
return EINVAL;
case B_GET_GEOMETRY:
case B_GET_BIOS_GEOMETRY:
if (data) {
device_geometry *geom = (device_geometry *)data;
geom->bytes_per_sector = 256;
geom->sectors_per_track = 1;
geom->cylinder_count = cookie->dev->size / 256;
geom->head_count = 1;
geom->device_type = B_DISK;
geom->removable = false;
geom->read_only = false; // XXX
geom->write_once = false;
return B_OK;
}
return EINVAL;
case B_GET_MEDIA_STATUS:
if (data) {
*(status_t *)data = B_OK;
return B_OK;
}
return EINVAL;
case B_EJECT_DEVICE:
case B_LOAD_MEDIA:
return ENOSYS;
case B_FLUSH_DRIVE_CACHE: /* wait for request list to be empty ? */
default:
return ENOSYS;
}
return B_NOT_ALLOWED;
}
status_t nbd_read(cookie_t *cookie, off_t position, void *data, size_t *numbytes) {
*numbytes = 0;
return B_NOT_ALLOWED;
}
status_t nbd_write(cookie_t *cookie, off_t position, const void *data, size_t *numbytes) {
(void)cookie; (void)position; (void)data; (void)numbytes;
*numbytes = 0;
return EIO;
}
device_hooks nbd_hooks={
(device_open_hook)nbd_open,
nbd_close,
(device_free_hook)nbd_free,
(device_control_hook)nbd_control,
(device_read_hook)nbd_read,
(device_write_hook)nbd_write,
NULL,
NULL,
NULL,
NULL
};
#pragma mark ==== driver hooks ====
static const char *nbd_name[MAX_NBDS+1] = {
NULL
};
status_t
init_hardware (void)
{
return B_OK;
}
status_t
init_driver (void)
{
int i;
// load settings
for (i = 0; i < MAX_NBDS; i++) {
nbd_name[i] = malloc(DEVICE_NAME_MAX);
if (nbd_name[i] == NULL)
break;
sprintf(nbd_name[i], DEVICE_FMT, i);
}
nbd_name[i] = NULL;
return B_OK;
}
void
uninit_driver (void)
{
int i;
for (i = 0; i < MAX_NBDS; i++) {
free(nbd_name[i]);
}
}
const char**
publish_devices()
{
return nbd_name;
}
device_hooks*
find_device(const char* name)
{
return &nbd_hooks;
}
@@ -0,0 +1,46 @@
/*
* Network Block Device protocol
* Copyright 2006-2007, François Revol. All rights reserved.
* Distributed under the terms of the MIT License.
*
* references:
* include/linux/nbd.h
*/
enum {
NBD_CMD_READ = 0,
NBD_CMD_WRITE,
NBD_CMD_DISC
};
#define NBD_REQUEST_MAGIC 0x25609513
#define NBD_REPLY_MAGIC 0x67446698
/* in network byte order */
struct nbd_request {
uint32 magic; /* REQUEST_MAGIC */
uint32 type;
uint64 handle; //char handle[8];
uint64 from;
uint32 len;
} _PACKED;
/* in network byte order */
struct nbd_reply {
uint32 magic; /* REPLY_MAGIC */
uint32 error;
uint64 handle; //char handle[8];
} _PACKED;
/* initialization protocol (ENBD ? or at least Linux specific ?) */
#define NBD_INIT_PASSWD "NBDMAGIC"
#define NBD_INIT_MAGIC 0x0000420281861253LL
/* in network byte order */
struct nbd_init_packet {
uint8 passwd[8]; /* "NBDMAGIC" */
uint64 magic; /* INIT_MAGIC */
uint64 device_size; /* size in bytes */
uint8 dummy[128]; /* reserved for future use */
} _PACKED;