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:
@@ -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;
|
||||
Reference in New Issue
Block a user