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