Discuss: Mount any VBox-compatible disk image on the host

Discussions related to using VirtualBox on Linux hosts.
Post Reply
h2o
Posts: 40
Joined: 9. Feb 2009, 04:46

Discuss: Mount any VBox-compatible disk image on the host

Post by h2o »

In this thread, you can discuss anything you have to say about this howto.

This used to be the main thread for the howto, but because it's hard for people to find downloads and updates with comments interspersed with release announcements, I decided to change this into a discussion thread and start a new thread for the tutorial and release. Hope you understand :D
Last edited by h2o on 11. May 2009, 06:14, edited 10 times in total.
h2o
Posts: 40
Joined: 9. Feb 2009, 04:46

Post by h2o »

UPDATE 2/12/09: NEW, updated FUSE-based version available here: http://forums.virtualbox.org/viewtopic. ... 9679#59679

And here it is:

UPDATE 2/10/09: used the new VBoxHDD-new.h api, added -DOLDAPI for compatibility, plugged small memory leak, added new option for choosing image type, prevent mapping non-existent partitions

OBSOLETE, DON'T USE

Code: Select all

/* Original author: h2o on forums.virtualbox.org			*
 * http://forums.virtualbox.org/viewtopic.php?p=59275			*
 * vdimount.c - tool for mounting VDI files				*/

/*									*
 * Available compile flags:						*
 * 	-DNO_VBOX_HDRS: don't use the VBox headers			*
 * 		(useful if you don't have them)				*
 *	-DOLDAPI: use old VBoxHDD.h api (default uses VBoxHDD-new.h)	*
 *									*/


/* This program is free software: you can redistribute it and/or modify	*
 * it under the terms of the GNU General Public License as published by	*
 * the Free Software Foundation, either version 3 of the License, or	*
 * (at your option) any later version.					*
 *									*
 * This program is distributed in the hope that it will be useful,	*
 * but WITHOUT ANY WARRANTY; without even the implied warranty of	*
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the	*
 * GNU General Public License for more details.				*
 *									*
 * You should have received a copy of the GNU General Public License	*
 * along with this program. If not, see <http://www.gnu.org/licenses/>.	*/

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <endian.h>
#include <errno.h>
#include <pthread.h>
#include <linux/nbd.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <netinet/in.h>

#warning "This program is obsolete, use the fuse-based version (vdfuse) instead"

#ifdef __GNUC__
#define UNUSED __attribute__ ((unused))
#else
#define UNUSED
#endif

// macro to convert uint64_t's to network order
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define ntohll __bswap_64
#else
#define ntohll
#endif
#define htonll ntohll

#ifdef NO_VBOX_HDRS
#define VBOX_SUCCESS(i) (__builtin_expect (i >= 0, 1))
#define VBOX_FAILURE(i) (!VBOX_SUCCESS (i))
#endif

#ifdef OLDAPI

#ifdef NO_VBOX_HDRS
// from VBoxHDD.h

// (P)VDIDISK
struct VDIDISK;
typedef struct VDIDISK VDIDISK;
typedef VDIDISK *PVDIDISK;

// Flags
#define VDI_OPEN_FLAGS_NORMAL (0)
#define VDI_OPEN_FLAGS_READONLY (1)

// Functions
extern PVDIDISK VDIDiskCreate (void);
extern int VDIDiskOpenImage (PVDIDISK, const char *, unsigned);
extern void VDIDiskCloseImage (PVDIDISK);
extern int VDIDiskRead (PVDIDISK, uint64_t, void *, size_t);
extern int VDIDiskWrite (PVDIDISK, uint64_t, const void *, size_t);
extern uint64_t VDIDiskGetSize (PVDIDISK);

#else // NO_VBOX_HDRS
#define IN_RING3
#include <VBox/VBoxHDD.h>
#endif // NO_VBOX_HDRS

#else // OLDAPI

#ifdef NO_VBOX_HDRS
// from VBoxHDD-new.h

// VDINTERFACETYPE
typedef enum VDINTERFACETYPE
{
	VDINTERFACETYPE_FIRST = 0,
	VDINTERFACETYPE_ERROR = VDINTERFACETYPE_FIRST,
	VDINTERFACETYPE_ASYNCIO,
	VDINTERFACETYPE_PROGRESS,
	VDINTERFACETYPE_CONFIG,
	VDINTERFACETYPE_INVALID
} VDINTERFACETYPE;

// (P)VDINTERFACE
typedef struct VDINTERFACE
{
	const char *pszInterfaceName;
	uint32_t cbSize;
	struct VDINTERFACE *pNext;
	VDINTERFACETYPE enmInterface;
	void *pvUser;
	void *pCallbacks;
} VDINTERFACE, *PVDINTERFACE;

// (P)VBOXHDD
struct VBOXHDD;
typedef struct VBOXHDD VBOXHDD;
typedef VBOXHDD *PVBOXHDD;

// Flags
#define VD_OPEN_FLAGS_NORMAL	0
#define VD_OPEN_FLAGS_READONLY	1

// Functions
extern int VDCreate(PVDINTERFACE, PVBOXHDD *);
extern int VDOpen(PVBOXHDD, const char *, const char *, unsigned, PVDINTERFACE);
extern uint64_t VDGetSize(PVBOXHDD, unsigned);
extern int VDRead(PVBOXHDD, uint64_t, void *, size_t);
extern int VDWrite(PVBOXHDD, uint64_t, const void *, size_t);
extern int VDClose(PVBOXHDD, int /* should be bool */);

#else // NO_VBOX_HDRS
#define IN_RING3
#include <VBox/VBoxHDD-new.h>
#endif // NO_VBOX_HDRS

#endif // OLDAPI

#define SINSIZE sizeof (struct sockaddr_in)

// taken from cliserv.h in nbd package
#define INIT_PASSWD "NBDMAGIC"
#define BUFSIZE 1048576
#define NBD_FLAG_HAS_FLAGS (1)
#define NBD_FLAG_READ_ONLY (2)
uint64_t clientmagic = 0x00420281861253LL;

typedef struct
{
	int pn;			// partition #
	int port;		// port #

	int sock;		// socket #
	struct sockaddr_in sin;

	uint64_t offset;	// offset into disk
	uint64_t size;		// size of partiton
} abinding;

// MBR entry structure
struct mbri
{
	uint8_t status;
	uint8_t shead;
	uint8_t ssector;
	uint8_t sbits;
	uint8_t type;
	uint8_t ehead;
	uint8_t esector;
	uint8_t ebits;
	uint32_t offset;
	uint32_t size;
};

// own implementation of GList from GLib
typedef struct _dlist dlist;

struct _dlist
{
	void *data;
	dlist *next;
	dlist *prev;
};

// struct to pass args to server thread
struct serverarg
{
	dlist *dl;
	abinding *bn;
};

struct sigaction siga;

void *server_main (void *);
void cleanup (int);
void start_server (int);
int vread (abinding *, uint64_t, uint32_t, void *);
int vwrite (abinding *, uint64_t, uint32_t, const void *);
void timeout (int);

dlist *list_start (dlist *list)
{
	while (list && list->prev)
		list = list->prev;
	return list;
}

dlist *list_end (dlist *list)
{
	while (list && list->next)
		list = list->next;
	return list;
}

int list_len (dlist *list)
{
	int i = 0;
	list = list_start (list);
	do
	{
		i++;
	}
	while (list && (list = list->next));

	return i;
}

dlist *list_add (dlist *list, void *data)
{
	if (list)
		list = list_end (list);
	dlist *newlist = malloc (sizeof (dlist));
	newlist->data = data;
	newlist->next = NULL;
	newlist->prev = list;
	if (list)
		list->next = newlist;
	return list_start (newlist);
}

dlist *list_del (dlist *item)
{
	dlist *next = item->next;
	dlist *prev = item->prev;
	if (next)
		next->prev = prev;
	if (prev)
		prev->next = next;
	free (item);

	if (next)
		return list_start (next);
	else if (prev)
		return list_start (prev);
	else
		return NULL;
}

// printf to stderr
const char *errout_pre = "ERROR: ";
void errout (const char *format, ...)
{
	char *tmpfmt = malloc (strlen (errout_pre) + strlen (format) + 2);
	strcpy (tmpfmt, errout_pre);
	strcat (tmpfmt, format);
	strcat (tmpfmt, "\n");

	va_list ap;
	va_start (ap, format);
	vfprintf (stderr, tmpfmt, ap);
	va_end (ap);
	free (tmpfmt);
	fflush (stderr);
}

int verbose = 0;
// verbose printf
void vbprintf (const char *format, ...)
{
	if (!verbose)
		return;
	va_list ap;
	va_start (ap, format);
	vprintf (format, ap);
	va_end (ap);
	fflush (stdout);
}

dlist *maplist;
dlist *thrs;

#ifdef OLDAPI
PVDIDISK vdidisk;
#else
VDINTERFACE dInterface;
PVBOXHDD dDisk;
#endif

int readonly;
#define BLOCKSIZE 512
pthread_mutex_t disk_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t sock_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t sock_mutex = PTHREAD_MUTEX_INITIALIZER;
struct sigaction blocksig;
struct sigaction freesig;
// valid backends
char *validBackends[] = {"VDI", "VMDK", "VHD", "raw"};

// adds partition->port mapping
int add_map (char *v)
{
	char *eqi = strchr (v, '=');
	if (!eqi)
	{
		errout ("invalid argument \"%s\"", v);
		return -1;
	}
	char *pn = v;
	char *port = eqi + 1;
	*eqi = 0;

	int pni;
	if (strcmp (pn, "raw") == 0)
		pni = -1;
	else
		pni = strtol (pn, 0, 10);

	int porti = strtol (port, 0, 10);

	if (pni == 0 || porti == 0)
	{
		errout ("invalid partition number %d or port number %d", pni, porti);
		return -1;
	}

	abinding *binding = malloc (sizeof (abinding));
	binding->pn = pni;
	binding->port = porti;

	maplist = list_add (maplist, binding);

	return 0;
}

// processes logical partitions (does this work???)
int pnse (uint64_t offset)
{
	int pn = 5;
	while (offset)
	{
		char buf[512];

#ifdef OLDAPI
		VDIDiskRead (vdidisk, offset, buf, 512);
#else
		VDRead (dDisk, offset, buf, 512);
#endif

		struct mbri *entry_a = (struct mbri *)(buf + 446);
		struct mbri *entry_b = (struct mbri *)(buf + 462);
		uint16_t *sig = (uint16_t *)(buf + 510);
		if (*sig != 0xaa55)
		{
			errout ("invalid ebr signature at offset %llx", offset);
			return -1;
		}

		uint64_t offset64 = (uint64_t)entry_a->offset;
		offset64 *= BLOCKSIZE;
		uint64_t size64 = (uint64_t)entry_a->size;
		size64 *= BLOCKSIZE;

		dlist *mm = maplist;
		while (mm)
		{
			abinding *bn = mm->data;
			if (bn->pn == pn)
			{
				bn->offset = offset64;
				bn->size = size64;
			}
			mm = mm->next;
		}
		offset = entry_b->offset;
		pn++;
	}
	return 0;
}

// processes partiton table
int pns ()
{
	char dmbr[512];

#ifdef OLDAPI
	VDIDiskRead (vdidisk, 0, dmbr, 512);
#else
	VDRead (dDisk, 0, dmbr, 512);
#endif

	struct mbri *pts = (struct mbri *)(dmbr + 446);

	uint16_t *sig;
	sig = (uint16_t *)(dmbr + 510);
	if (*sig != 0xaa55)
	{
		errout ("invalid mbr signature");
		return -1;
	}

	int i = 0;
	dlist *mm = maplist;
	abinding *bn;
	while (i < 4)
	{
		int pn = i + 1;
		uint64_t offset64 = (uint64_t)pts[i].offset;
		offset64 *= BLOCKSIZE;
		uint64_t size64 = (uint64_t)pts[i].size;
		size64 *= BLOCKSIZE;
		while (mm)
		{
			bn = mm->data;
			if (bn->pn == pn)
			{
				bn->offset = offset64;
				bn->size = size64;
			}
			mm = mm->next;
		}
		if (pts[i].type == 5)
		{
			uint64_t extpart = (uint64_t) pts[i].offset;
			extpart = extpart * BLOCKSIZE;
			if (pnse (extpart) < 0)
				return -1;
		}
		i++;
	}
	mm = maplist;
	while (mm)
	{
		bn = mm->data;
		if (bn->pn == -1)
		{
			bn->offset = 0;

#ifdef OLDAPI
			bn->size = VDIDiskGetSize (vdidisk);
#else
			bn->size = VDGetSize (dDisk, 0);
#endif

			break;
		}
		mm = mm->next;
	}
	return 0;
}

int main (int c, char **v)
{
	if (c < 4)
	{
		printf ("Usage: %s [-r] [-v] [-d] [-t type] -f image-file partiton-number=port-number "
			"[partition-#=port-#] [...]\n"
			"\t-r readonly\n"
			"\t-f path to image file\n"
			"\t-d daemonize\n"
#ifndef OLDAPI
			"\t-t image type (VDI, VMDK, VHD, raw) (default: VDI)\n"
#endif
			"\t-v verbose\n"
			"To use the entire disk use \"raw\" as the partition number.\n\n"
			"Partition 1 is first partiton; partition 5 is first logical partition.\n"
			"Example:\tMap partition 1 to port 2001, partition 2 to port 2002:\n"
			"\t%s -f /path/to/image/file 1=2001 2=2002\n", v[0], v[0]);
		return 255;
	}
	char *imagefilename = NULL;
#ifndef OLDAPI
	char *diskType = "VDI";
#endif
	// parse args
	int dofork = 0;
	v++;
	while (*v)
	{
		if (strcmp (*v, "-r") == 0)
			readonly = 1;
		else if (strcmp (*v, "-f") == 0)
			imagefilename = *++v;
		else if (strcmp (*v, "-d") == 0)
			dofork = 1;
		else if (strcmp (*v, "-v") == 0)
			verbose = 1;
#ifndef OLDAPI
		else if (strcmp (*v, "-t") == 0)
			diskType = *++v;
#endif
		else
		{
			if (add_map (*v) < 0)
				return 1;
		}
		v++;
	}

	if (!maplist)
	{
		errout ("no partion->port mappings specified");
		return 1;
	}

	if (!imagefilename)
	{
		errout ("no image file specified");
		return 1;
	}

#ifndef OLDAPI
	unsigned int di = 0;
	int invalidBackend = 1;
	while (di < (sizeof (validBackends) / sizeof (char *)))
	{
		if (strcmp (validBackends[di], diskType) == 0)
		{
			invalidBackend = 0;
			break;
		}
		di++;
	}
	if (invalidBackend)
	{
		errout ("invalid disk type specified, valid types are VDI, VMDK, VHD, and raw.");
		return 1;
	}
#endif

	// open disk
#ifdef OLDAPI
	vdidisk = VDIDiskCreate ();
	int imageopen = VDI_OPEN_FLAGS_NORMAL;
	if (readonly)
		imageopen = VDI_OPEN_FLAGS_READONLY;
	int ret = VDIDiskOpenImage (vdidisk, imagefilename, imageopen);
#else
	dInterface.pszInterfaceName = "What's an interface?";
	dInterface.enmInterface = VDINTERFACETYPE_FIRST;
	VDCreate (&dInterface, &dDisk);
	int imageopen = VD_OPEN_FLAGS_NORMAL;
	if (readonly)
		imageopen = VD_OPEN_FLAGS_READONLY;
	int ret = VDOpen (dDisk, diskType, imagefilename, imageopen, &dInterface);
#endif
	if (VBOX_FAILURE (ret))
	{
		errout ("opening vbox image failed, errno %d", ret);
		return 1;
	}

	vbprintf ("image opened\n");

	if (pns () < 0)
		return 1;

	// setup poll structures
	struct pollfd *pfd = malloc (list_len (maplist) * sizeof (struct pollfd));
	int pfdl = 0;

	// bind / listen sockets
	dlist *mm = maplist;
	int i = 0;
	while (mm)
	{
		abinding *bn = mm->data;
		bn->sock = socket (AF_INET, SOCK_STREAM, 0);

		pfd[pfdl].fd = bn->sock;
		pfd[pfdl].events = POLLIN;
		pfdl++;

		memset (&bn->sin, 0, SINSIZE);
		bn->sin.sin_family = AF_INET;
		bn->sin.sin_port = htons (bn->port);
		bn->sin.sin_addr.s_addr = htonl (0x7f000001); /* 127.0.0.1 */

		ret = bind (bn->sock, (struct sockaddr *)&bn->sin, SINSIZE);
		if (ret < 0)
		{
			errout ("Failed binding socket: %m", errno);
			return 1;
		}
		listen (bn->sock, 1024);

		vbprintf ("Binding %d:\n\tPartition #: %d\n\tPort: %d\n\tOffset: %lld\n\tSize: %lld\n\n", i++, bn->pn, bn->port, bn->offset, bn->size);
		if (!bn->size)
		{
			errout ("Size of binding %d is zero, either partition %d does not exist, or the partition table is corrupt/not valid.", i, bn->pn);
			return 1;
		}
		
		mm = mm->next;
	}

	siga.sa_handler = cleanup;
	sigfillset (&siga.sa_mask);
	siga.sa_flags = 0;

	sigaction (SIGTERM, &siga, 0);
	sigaction (SIGINT, &siga, 0);
	blocksig.sa_handler = SIG_IGN;

	// daemonize
	if (dofork)
	{
		verbose = 0;
		int forkrc = fork ();
		if (forkrc < 0)
			errout ("failed to daemonize: %m", errno);
		else if (forkrc)
			return 0;
		else
		{
			int devnull = open ("/dev/null", O_RDWR);
			dup2 (devnull, 0);
			dup2 (devnull, 1);
			dup2 (devnull, 2);
			close (devnull);
		}
	}

	// MAIN LOOP
	int po;
	while (1)
	{
		po = poll (pfd, pfdl, 1000);
		if (po > 0)
		{
			i = 0;
			while (i < pfdl)
			{
				if (pfd[i].revents & POLLIN)
				{
					pthread_mutex_lock (&sock_mutex);
					start_server (pfd[i].fd);
					// Keep from starting multiple threads for one connection
					pthread_cond_wait (&sock_cond, &sock_mutex);
					pthread_mutex_unlock (&sock_mutex);
				}
				i++;
			}
		}
	}
}

void start_server (int fd)
{
	dlist *mm = maplist;
	abinding *bn;
	int notfound = 1;
	while (mm)
	{
		bn = mm->data;
		if (bn->sock == fd)
		{
			notfound = 0;
			break;
		}
		mm = mm->next;
	}

	if (notfound)
		return;

	vbprintf ("starting server for partition #%d\n", bn->pn);

	pthread_t *thr = malloc (sizeof (pthread_t));
	thrs = list_add (thrs, thr);
	struct serverarg *sarg = malloc (sizeof (struct serverarg));
	sarg->dl = list_end (thrs);
	sarg->bn = bn;
	pthread_create (thr, NULL, server_main, (void *)sarg);
}

// SIGPIPE-safe write
int swrite (int fd, const void *buf, int count)
{
	sigaction (SIGPIPE, &blocksig, &freesig);
	int ret = write (fd, buf, count);
	int oerrno = errno;
	sigaction (SIGPIPE, &freesig, &blocksig);
	errno = oerrno;
	return ret;
}

// negotiate function (taken from nbd-server)
int negotiate (int fd, abinding *bn)
{
	sigaction (SIGPIPE, &blocksig, &freesig);
	if (write (fd, INIT_PASSWD, 8) < 0)
	{
		errout ("negotiate: cannot write INIT_PASSWD: %m", errno);
		return -1;
	}

	uint64_t clientmagicout = htonll (clientmagic);
	if (write (fd, &clientmagicout, sizeof (clientmagic)) < 0)
	{
		errout ("negotiate: cannot write clientmagic: %m", errno);
		return -1;
	}

	uint64_t sizeout = htonll (bn->size);
	if (write (fd, &sizeout, sizeof (sizeout)) < 0)
	{
		errout ("negotiate: cannot write size: %m", errno);
		return -1;
	}

	uint32_t flags = NBD_FLAG_HAS_FLAGS;
	if (readonly)
		flags |= NBD_FLAG_READ_ONLY;
	flags = htonl (flags);
	if (write (fd, &flags, sizeof (flags)) < 0)
	{
		errout ("negotiate: cannot write flags: %m", errno);
		return -1;
	}

	char *nas[124];
	memset (nas, 0, 124);
	if (write (fd, nas, 124) < 0)
	{
		errout ("negotiate: cannot write zeroes: %m", errno);
		return -1;
	}

	sigaction (SIGPIPE, &freesig, &blocksig);
	return 0;
}

// macro for sending error packet
#define SERR(fd,reply,code) \
	{ \
		reply.error = htonl (code); \
		swrite (fd, &reply, sizeof (reply)); \
		reply.error = 0; \
		if (errno == EPIPE) \
			break;\
	}

void *server_main (void *sarg)
{
	struct serverarg *sargs = sarg;
	abinding *bn = sargs->bn;

	pthread_mutex_lock (&sock_mutex);
	int mycon = accept (bn->sock, 0, 0);
	pthread_cond_broadcast (&sock_cond);
	pthread_mutex_unlock (&sock_mutex);
	if (mycon < 0)
	{
		errout ("failed to accept connection on socket %d: %m", bn->sock, errno);
		goto out;
	}

	struct nbd_request request;
	struct nbd_reply reply;
	
	if (negotiate (mycon, bn) < 0)
		goto out;

	reply.magic = htonl (NBD_REPLY_MAGIC);
	reply.error = 0;

	while (1)
	{
		char buf[BUFSIZE];

		int reqread = read (mycon, &request, sizeof (request));
		if (reqread < 0)
			goto out;
		else if (reqread != sizeof (request))
		{
			errout ("bad packet: packet is %d bytes instead of %d", reqread, sizeof (request));
			SERR (mycon, reply, EINVAL);
			continue;
		}

		request.magic = ntohl (request.magic);
		request.type = ntohl (request.type);
		request.from = ntohll (request.from);
		request.len = ntohl (request.len);

		if (request.type == NBD_CMD_DISC)
		{
			vbprintf ("got disconnect command\n");
			break;
		}

		if (request.magic != NBD_REQUEST_MAGIC)
		{
			errout ("invalid request magic");
			SERR (mycon, reply, EINVAL);
			continue;
		}

		if (request.len > BUFSIZE + sizeof (reply))
		{
			errout ("request too big");
			SERR (mycon, reply, ENOBUFS);
			continue;
		}

		memcpy (reply.handle, request.handle, sizeof (reply.handle));

		if (request.from + request.len > bn->offset + bn->size)
		{
			errout ("request beyond end of device");
			SERR (mycon, reply, EINVAL);
			continue;
		}

		if (request.type == NBD_CMD_WRITE)
		{
			vbprintf ("got write command to write %d bytes at offset %lld on partition %d\n", request.len, request.from, bn->pn);

			if (readonly)
			{
				errout ("attempted write on readonly map");
				SERR (mycon, reply, EPERM);
				continue;
			}

			struct pollfd pfd;
			pfd.fd = mycon;
			pfd.events = POLLIN;
			if (poll (&pfd, 1, 5000) == 0)
			{
				errout ("read data timed out");
				SERR (mycon, reply, ETIMEDOUT);
				continue;
			}
			
			read (mycon, buf, request.len);

			if (vwrite (bn, request.from, request.len, buf) < 0)
			{
				errout ("problem writing to disk");
				SERR (mycon, reply, EIO);
				continue;
			}
			swrite (mycon, &reply, sizeof (reply));
			continue;
		}
		else if (request.type == NBD_CMD_READ)
		{
			vbprintf ("got read command to read %d bytes at offset %lld on partition %d\n", request.len, request.from, bn->pn);
			if (vread (bn, request.from, request.len, buf + sizeof (reply)) < 0)
			{
				errout ("problem reading from disk");
				SERR (mycon, reply, EIO);
				continue;
			}
			memcpy (buf, &reply, sizeof (reply));
			swrite (mycon, buf, request.len + sizeof (reply));
			continue;
		}
		else
		{
			errout ("invalid command");
			SERR (mycon, reply, EINVAL);
			continue;
		}
	}

	out:
	close (mycon);
	free (sargs->dl->data); // free pthread_t
	thrs = list_del (sargs->dl); // free list item
	free (sarg); // free server args
	vbprintf ("closing server for partition #%d\n", bn->pn);
	pthread_exit (0);
}

int vread (abinding *bn, uint64_t from, uint32_t len, void *buf)
{
	pthread_mutex_lock (&disk_mutex);
#ifdef OLDAPI
	int ret = VDIDiskRead (vdidisk, from + bn->offset, buf, len);
#else
	int ret = VDRead (dDisk, from + bn->offset, buf, len);
#endif
	pthread_mutex_unlock (&disk_mutex);
	if (VBOX_SUCCESS (ret))
		return 0;
	else
		return -1;
}

int vwrite (abinding *bn, uint64_t from, uint32_t len, const void *buf)
{
	pthread_mutex_lock (&disk_mutex);
#ifdef OLDAPI
	int ret = VDIDiskWrite (vdidisk, from + bn->offset, buf, len);
#else
	int ret = VDWrite (dDisk, from + bn->offset, buf, len);
#endif
	pthread_mutex_unlock (&disk_mutex);
	if (VBOX_SUCCESS (ret))
		return 0;
	else
		return -1;
}

// called on SIGINT / SIGTERM
void cleanup (int s UNUSED)
{
#ifdef OLDAPI
	VDIDiskCloseImage (vdidisk);
#else
	VDClose (dDisk, 0);
#endif
	dlist *mm = maplist;
	while (mm)
	{
		abinding *bn = mm->data;
		close (bn->sock);
		mm = mm->next;
	}
	exit (0);
}
Last edited by h2o on 5. Nov 2009, 06:14, edited 5 times in total.
h2o
Posts: 40
Joined: 9. Feb 2009, 04:46

Post by h2o »

Oops :oops: I reposted the above code :?
Last edited by h2o on 11. Feb 2009, 04:35, edited 1 time in total.
TerryE
Volunteer
Posts: 3572
Joined: 28. May 2008, 08:40
Primary OS: Ubuntu other
VBox Version: PUEL
Guest OSses: Ubuntu 10.04 & 11.10, both Svr&Wstn, Debian, CentOS
Contact:

Post by TerryE »

AFAIK from 2.1 as part of the move to a more format neutral implementation, the public interface to VBoxDD.so VBoxHDD.h has been superceded by VBoxHDD-new.h (which also documents this API). This defines new entries and API so, for example, VDIDiskRead has been replaced by VDRead. The main difference with the VD* routines is that they work on all supported image formats (VDI, VMDK, raw VMDK, VHD).

So my recommendation would be to switch to using the new interface.

As far as the MBR decode that you include, it is just that; it does not decode the EBS. Why not just use the code in the routine VBoxInternalManage.cpp:partRead as a template?

Two niggles: (i) I see you belong to the school of programming: documentation and comments are for wusses; and (ii) I am not sure why you bother to code up your own list processing routines rather than just use the standard STL ones.
Read the Forum Posting Guide
Google your Q site:VirtualBox.org or search for the answer before posting.
echo6
Posts: 9
Joined: 13. Oct 2007, 19:07

Post by echo6 »

I get the following errors when trying to compile, I don't have the headers I'm not using the OSE version cause I need usb2 support.

Code: Select all

/tmp/ccjtlwWP.o: In function `pnse':
vdimount.c:(.text+0x3ba): undefined reference to `VDIDiskRead'
/tmp/ccjtlwWP.o: In function `pns':
vdimount.c:(.text+0x5ac): undefined reference to `VDIDiskRead'
vdimount.c:(.text+0x7ee): undefined reference to `VDIDiskGetSize'
/tmp/ccjtlwWP.o: In function `main':
vdimount.c:(.text+0x9c3): undefined reference to `VDIDiskCreate'
vdimount.c:(.text+0x9fb): undefined reference to `VDIDiskOpenImage'
/tmp/ccjtlwWP.o: In function `vread':
vdimount.c:(.text+0x18e6): undefined reference to `VDIDiskRead'
/tmp/ccjtlwWP.o: In function `vwrite':
vdimount.c:(.text+0x1970): undefined reference to `VDIDiskWrite'
/tmp/ccjtlwWP.o: In function `cleanup':
vdimount.c:(.text+0x19b7): undefined reference to `VDIDiskCloseImage'
collect2: ld returned 1 exit status
TerryE
Volunteer
Posts: 3572
Joined: 28. May 2008, 08:40
Primary OS: Ubuntu other
VBox Version: PUEL
Guest OSses: Ubuntu 10.04 & 11.10, both Svr&Wstn, Debian, CentOS
Contact:

Post by TerryE »

echo6 wrote:I get the following errors when trying to compile...
That's because as I said, the current version of VBoxDD.so has retired the VBoxHDD.h interface, and VDRead replaces VDIDiskRead, etc.
Read the Forum Posting Guide
Google your Q site:VirtualBox.org or search for the answer before posting.
h2o
Posts: 40
Joined: 9. Feb 2009, 04:46

Post by h2o »

TerryE wrote:This defines new entries and API so, for example, VDIDiskRead has been replaced by VDRead. The main difference with the VD* routines is that they work on all supported image formats (VDI, VMDK, raw VMDK, VHD).
I used vditool.cpp as a template.
TerryE wrote:Two niggles: (i) I see you belong to the school of programming: documentation and comments are for wusses; and (ii) I am not sure why you bother to code up your own list processing routines rather than just use the standard STL ones.
This was just a project I did out of boredom, and yes, I am a total beginner in C and I don't know a cent of C++. The list functions are based on GLib, and since I don't know C++, I had to use my own.
h2o
Posts: 40
Joined: 9. Feb 2009, 04:46

Post by h2o »

I updated it for the new api, and I added the comments that TerryE wanted:

http://forums.virtualbox.org/viewtopic. ... 9276#59276
TerryE
Volunteer
Posts: 3572
Joined: 28. May 2008, 08:40
Primary OS: Ubuntu other
VBox Version: PUEL
Guest OSses: Ubuntu 10.04 & 11.10, both Svr&Wstn, Debian, CentOS
Contact:

Post by TerryE »

I am a bit buried at the moment, so I can't look at this now, but I've bookmarked it to come back to. If you've just learnt C, where did you come up with the idea of using NBD?
Read the Forum Posting Guide
Google your Q site:VirtualBox.org or search for the answer before posting.
h2o
Posts: 40
Joined: 9. Feb 2009, 04:46

Post by h2o »

OK, I have to admit, I didn't "just" learned C; I've gotten sort of good at it; it's just that I don't have good programming practices (comments, etc.) The NBD idea came from vmware-loop, and quite a lot of the code is based on nbd-server. I didn't want to learn kernel programming nor did I want to rewrite every file system with FUSE, so I chose NBD.
TerryE
Volunteer
Posts: 3572
Joined: 28. May 2008, 08:40
Primary OS: Ubuntu other
VBox Version: PUEL
Guest OSses: Ubuntu 10.04 & 11.10, both Svr&Wstn, Debian, CentOS
Contact:

Post by TerryE »

I thought about using Fuse to create a file system which exposed the partitions as separate files Partition0, etc. within the root of the FS which are a block image of the virtualised partition on the disk and then just use a loop mount to mount the partition that you need. Writing a Fuse FS like this is pretty straight forward.
Read the Forum Posting Guide
Google your Q site:VirtualBox.org or search for the answer before posting.
h2o
Posts: 40
Joined: 9. Feb 2009, 04:46

Post by h2o »

TerryE wrote:I thought about using Fuse to create a file system which exposed the partitions as separate files Partition0, etc. within the root of the FS which are a block image of the virtualised partition on the disk and then just use a loop mount to mount the partition that you need. Writing a Fuse FS like this is pretty straight forward.
I made another tool based on TerryE's idea.

Pre-1) Get the tools
On Ubuntu:

Code: Select all

apt-get install build-essential libfuse-dev
1) Compile it
Download it, compile with:

Code: Select all

gcc vdifuse.c -o vdifuse `pkg-config --cflags --libs fuse` -l:/path/to/virtualbox/VBoxDD.so -Wl,-rpath,/path/to/virtualbox -pthread -I/path/to/virtualbox/headers/include
Replace /path/to/virtualbox with your installation of virtualbox (it has a file named VBoxDD.so) and /path/to/virtualbox/headers with the location of your VirtualBox headers (it has a folder named Vbox). If you don't have your headers, compile with:

Code: Select all

gcc vdifuse.c -o vdifuse `pkg-config --cflags --libs fuse` -l:/path/to/virtualbox/VBoxDD.so -Wl,-rpath,/path/to/virtualbox -pthread -DNO_VBOX_HDRS
If you are getting compile errors because you are using an old version of VirtualBox (before 2.0.0) add the -DOLDAPI flag to the command. You will not be able to mount VHD and VMDK images.

2) Mount the image

Code: Select all

vdifuse options -f image-file mountpoint
Available options:

Code: Select all

-r: readonly mode
-t type: choose image type (auto, VDI, VMDK, VHD, raw)
-a: allow all users to use the disk
-aw: allow all users to modify the disk
-g: run in foreground
-v: verbose
-d: debug fuse
You will now have a bunch of files in mountpoint.
  • EntireDisk: gives you the raw image of a disk. Useful for partitioning tools such as GParted or fdisk (but remember to restart vdifuse after modifying the partitions).
  • Partition[1-4]: images of each primary partition.
  • Partition[5+]: images of each logical partition.
3) Mount the partition

Code: Select all

modprobe loop (if your loop is in a module)
mount -o loop /path/to/partitions/PartitionX /mount/point
Like the first one, this tool has not been thoroughly tested, and I WILL NOT BE RESPONSIBLE CAUSED BY THIS PROGRAM. Use at your own risk. If in doubt, use the readonly (-r) flag.

Unlike the NBD based tool, this (supposedly) works with any operating system that supports FUSE.
h2o
Posts: 40
Joined: 9. Feb 2009, 04:46

Post by h2o »

And here's the FUSE version:

Update 2/13/09: fix things up for big endian systems

Code: Select all

/* Original author: h2o on forums.virtualbox.org			*
 * http://forums.virtualbox.org/viewtopic.php?p=59275			*
 * vdifuse.c - tool for mounting VDI/VMDK/VHD files			*/

/*									*
 * Available compile flags:						*
 * 	-DNO_VBOX_HDRS: don't use the VBox headers			*
 * 		(useful if you don't have them)				*
 *	-DOLDAPI: use old VBoxHDD.h api (default uses VBoxHDD-new.h)	*
 *									*/


/* This program is free software: you can redistribute it and/or modify	*
 * it under the terms of the GNU General Public License as published by	*
 * the Free Software Foundation, either version 3 of the License, or	*
 * (at your option) any later version.					*
 *									*
 * This program is distributed in the hope that it will be useful,	*
 * but WITHOUT ANY WARRANTY; without even the implied warranty of	*
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the	*
 * GNU General Public License for more details.				*
 *									*
 * You should have received a copy of the GNU General Public License	*
 * along with this program. If not, see <http://www.gnu.org/licenses/>.	*/

#define FUSE_USE_VERSION 26
#define FILE_OFFSET_BITS 64
#include <limits.h>
#include <fuse.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

#define IN_RING3

#ifdef NO_VBOX_HDRS // don't use vbox headers

#define VBOX_SUCCESS(i) (__builtin_expect (i >= 0, 1))
#define VBOX_FAILURE(i) (!VBOX_SUCCESS (i))

#ifdef OLDAPI

// PVDIDISK
typedef struct VDIDISK *PVDIDISK;

// Flags
#define VDI_OPEN_FLAGS_NORMAL (0)
#define VDI_OPEN_FLAGS_READONLY (1)

// Functions
extern PVDIDISK VDIDiskCreate (void);
extern int VDIDiskOpenImage (PVDIDISK, const char *, unsigned);
extern void VDIDiskCloseImage (PVDIDISK);
extern int VDIDiskRead (PVDIDISK, uint64_t, void *, size_t);
extern int VDIDiskWrite (PVDIDISK, uint64_t, const void *, size_t);
extern uint64_t VDIDiskGetSize (PVDIDISK);

#else // OLDAPI

// Flags
#define VD_OPEN_FLAGS_NORMAL 0
#define VD_OPEN_FLAGS_READONLY 1

// (P)VDINTERFACE
typedef struct VDINTERFACE
{
	const char *pszInterfaceName;
	uint32_t cbSize;
	struct VDINTERFACE *pNext;
	enum { VDINTERFACETYPE_FIRST = 0 } enmInterface;
	void *pvUser;
	void *pCallbacks;
} VDINTERFACE, *PVDINTERFACE;

// PVBOXHDD
typedef struct VBOXHDD *PVBOXHDD;

extern int VDCreate(PVDINTERFACE, PVBOXHDD *);
extern int VDOpen(PVBOXHDD, const char *, const char *, unsigned, PVDINTERFACE);
extern uint64_t VDGetSize(PVBOXHDD, unsigned);
extern int VDRead(PVBOXHDD, uint64_t, void *, size_t);
extern int VDWrite(PVBOXHDD, uint64_t, const void *, size_t);
extern int VDClose(PVBOXHDD, int /* should be bool */);

#endif // OLDAPI


#else // NO_VBOX_HDRS

#ifdef OLDAPI
#include <VBox/VBoxHDD.h>
#else // OLDAPI
#include <VBox/VBoxHDD-new.h>
#endif

#endif // NO_VBOX_HDRS

#define BLOCKSIZE 512

#ifdef __GNUC__
#define UNUSED __attribute__ ((unused))
#else
#define UNUSED
#endif

// own version of GLib's GList
typedef struct _dlist dlist;
struct _dlist
{
	void *data;
	dlist *next;
	dlist *prev;
};

// sort of like g_list_append
dlist *list_add (dlist *list, void *data)
{
	if (list)
		while (list && list->next)
			list = list->next;
	dlist *newlist = malloc (sizeof (dlist));
	newlist->data = data;
	newlist->next = NULL;
	newlist->prev = list;
	if (list)
		list->next = newlist;
	while (newlist && newlist->prev)
		newlist = newlist->prev;
	return newlist;
}

// printf to stderr
const char *errout_pre = "ERROR: ";
void errout (const char *format, ...)
{
	char *tmpfmt = malloc (strlen (errout_pre) + strlen (format) + 2);
	strcpy (tmpfmt, errout_pre);
	strcat (tmpfmt, format);
	strcat (tmpfmt, "\n");

	va_list ap;
	va_start (ap, format);
	vfprintf (stderr, tmpfmt, ap);
	va_end (ap);
	free (tmpfmt);
	fflush (stderr);
}

// verbose printf
int verbose = 0;
void vbprintf (const char *format, ...)
{
	if (!verbose)
		return;
	char *tmpfmt = malloc (strlen (format) + 2);
	strcpy (tmpfmt, format);
	strcat (tmpfmt, "\n");
	va_list ap;
	va_start (ap, format);
	vprintf (tmpfmt, ap);
	va_end (ap);
	free (tmpfmt);
	fflush (stdout);
}

// partition table entry format
struct mbri
{
	uint8_t status;
	uint8_t shead;
	uint8_t ssector;
	uint8_t sbits;
	uint8_t type;
	uint8_t ehead;
	uint8_t esector;
	uint8_t ebits;
	uint32_t offset;
	uint32_t size;
};

typedef struct
{
	int pn;			// partition #
	uint64_t offset;	// offset into disk
	uint64_t size;		// size of partiton
} apart;

dlist *partlist = NULL;

int foreground = 0;
int readonly = 0;
int allowall = 0; // allow all users to read from disk
int allowallw = 0; // allow all users to write to disk
uid_t myuid = 0;
gid_t mygid = 0;
uint64_t disksize = 0;

char *validBackends[] = {"auto", "VDI", "VMDK", "VHD", "raw"};

#define WHOLEPART "EntireDisk"

#ifdef OLDAPI
PVDIDISK vdidisk;
#else
VDINTERFACE dInterface;
PVBOXHDD dDisk;
#endif

// parse extended partitions
int pnse (uint64_t offset)
{
	int pn = 5;
	while (1)
	{
		char buf[66];

#ifdef OLDAPI
		VDIDiskRead (vdidisk, offset + 446, buf, 66);
#else
		VDRead (dDisk, offset + 446, buf, 66);
#endif

		struct mbri *entry_a = (struct mbri *)buf;
		struct mbri *entry_b = (struct mbri *)(buf + 16);
		char *sig = buf + 64;
		if (sig[0] != (char)0x55 || sig[1] != (char)0xaa)
		{
			errout ("invalid ebr signature at offset %llx", offset);
			return -1;
		}

		uint64_t offset64 = (uint64_t)entry_a->offset;
		offset64 *= BLOCKSIZE;
		uint64_t size64 = (uint64_t)entry_a->size;
		size64 *= BLOCKSIZE;

		apart *mypart = malloc (sizeof (apart));
		mypart->pn = pn;
		mypart->offset = offset + offset64;
		mypart->size = size64;
		partlist = list_add (partlist, mypart);

		vbprintf ("Partition %d (logical parition %d):\n\tOffset: %lld bytes\n\tSize: %lld bytes\n", pn, pn - 4, offset64, size64);

		uint64_t nextoffset = (uint64_t)entry_b->offset;
		if (nextoffset == 0)
			break;
		nextoffset *= BLOCKSIZE;
		offset += nextoffset;
		pn++;
	}
	return 0;
}

// parse primary partitions
int pns ()
{
	char dmbr[66];

#ifdef OLDAPI
	VDIDiskRead (vdidisk, 446, dmbr, 66);
#else
	VDRead (dDisk, 446, dmbr, 66);
#endif

	struct mbri *pts = (struct mbri *)dmbr;

	char *sig = dmbr + 64;
	if (sig[0] != (char)0x55 || sig[1] != (char)0xaa)
	{
		errout ("invalid mbr signature");
		return -1;
	}

	int i = 0;
	while (i < 4)
	{
		int pn = i + 1;
		uint64_t offset64 = (uint64_t)pts[i].offset;
		offset64 *= BLOCKSIZE;
		uint64_t size64 = (uint64_t)pts[i].size;
		size64 *= BLOCKSIZE;

		if (size64 == 0)
			goto end;

		apart *mypart = malloc (sizeof (apart));
		mypart->pn = pn;
		mypart->offset = offset64;
		mypart->size = size64;
		partlist = list_add (partlist, mypart);

		if (pts[i].type == 5)
			if (pnse (offset64) < 0)
				return -1;
		vbprintf ("Partition %d:\n\tOffset: %lld bytes\n\tSize: %lld bytes\n", pn, offset64, size64);
		end:
		i++;
	}
	return 0;
}

// detects type of virtual image
int detectDiskType (char **disktype, char *filename)
{
	int fd = open (filename, O_RDONLY);
	char buf[8];
	read (fd, buf, sizeof (buf));
	if (strcmp (buf, "conectix") == 0)
	{
		*disktype = "VHD";
		vbprintf ("disktype is VHD");
		return 0;
	}
	buf[4] = 0;
	if (strcmp (buf, "VMDK") == 0 || strcmp (buf, "KDMV") == 0)
	{
		*disktype = "VMDK";
		vbprintf ("disktype is VMDK");
		return 0;
	}
	buf[3] = 0;
	if (strcmp (buf, "<<<") == 0)
	{
		*disktype = "VDI";
		vbprintf ("disktype is VDI");
		return 0;
	}
	errout ("cannot autodetect disk type; please use the -t option.");
	return -1;
}

// gets partition # from fuse filename
int getpn (const char *c)
{
	int ret;
	if (strcmp (c, "/" WHOLEPART) == 0)
		return 0;
	else if (sscanf (c, "/Partition%d", &ret) > 0)
		return ret;
	else
		return -2;
}

// gets apart entry from fuse filename
apart *getpart (const char *c)
{
	int pn = getpn (c);
	if (pn == -2)
		return NULL;
	dlist *pm = partlist;
	while (pm)
	{
		if (((apart *)(pm->data))->pn == pn)
			return (apart *)pm->data;
		pm = pm->next;
	}
	return NULL;
}

int my_access (const char *c, int i)
{
	vbprintf ("access: %s, flags=%d", c, i);
	if (strcmp ("/", c) == 0)
		return 0;
	int partnum = getpn (c);
	if (partnum == -2)
		return -ENOENT;

	int wr = (i & W_OK) ? 1 : 0;

	dlist *pm = partlist;
	// check if partitions exists
	int partexists = 0;
	while (pm)
	{
		apart *mypart = (apart *)pm->data;
		if (mypart->pn == partnum)
		{
			partexists = 1;
			break;
		}
		pm = pm->next;
	}

	if (!partexists)
		return -ENOENT;

	struct fuse_context *ctxt = fuse_get_context ();

	if (readonly && wr)
		return -EROFS;

	if
	(
		ctxt->uid == 0 ||
		(allowall && !wr) ||
		allowallw ||
		ctxt->uid == myuid
	)
		return 0;
	else
		return -EACCES;
}

int my_open (const char *c, struct fuse_file_info *i)
{
	vbprintf ("open: %s", c);
	int aflags = F_OK;
	if (i->flags & O_WRONLY)
		aflags |= W_OK;
	else if (i->flags & O_RDONLY)
		aflags |= R_OK;
	else if (i->flags & O_RDWR)
		aflags |= (W_OK | R_OK);

	int ret = my_access (c, aflags);
	if (ret < 0)
		return ret;

	return ret;
}

enum ioop_cmd
{
	IOOP_READ,
	IOOP_WRITE
};

// function for io operations with disk
pthread_mutex_t disk_mutex = PTHREAD_MUTEX_INITIALIZER;
int ioop (enum ioop_cmd cmd, apart *mypart, off_t offset, void *buf, size_t len)
{
	uint64_t totend = offset;
	if (totend > disksize)
		return -EINVAL;
	totend += (uint64_t)len;
	if (totend > disksize)
		len = (size_t)disksize - (size_t)offset;

	int ret;
	pthread_mutex_lock (&disk_mutex);
	if (cmd == IOOP_READ)
	{
#ifdef OLDAPI
		ret = VDIDiskRead (vdidisk, offset + mypart->offset, buf, len);
#else
		ret = VDRead (dDisk, offset + mypart->offset, buf, len);
#endif
	}
	else if (cmd == IOOP_WRITE)
	{
#ifdef OLDAPI
		ret = VDIDiskWrite (vdidisk, offset + mypart->offset, buf, len);
#else
		ret = VDWrite (dDisk, offset + mypart->offset, buf, len);
#endif
	}
	else
		return -EINVAL;
	pthread_mutex_unlock (&disk_mutex);

	if (VBOX_SUCCESS (ret))
		return len;
	else
		return -EIO;
}

int my_read (const char *c, char *out, size_t len, off_t offset, struct fuse_file_info *i UNUSED)
{
	vbprintf ("read: %s, offset=%lld, length=%d", c, offset, len);
	apart *mypart = getpart (c);
	if (!mypart)
		return -ENOENT;

	return ioop (IOOP_READ, mypart, offset, out, len);
}

int my_write (const char *c, const char *in, size_t len, off_t offset, struct fuse_file_info *i UNUSED)
{
	vbprintf ("write: %s, offset=%lld, length=%d", c, offset, len);
	apart *mypart = getpart (c);
	if (!mypart)
		return -ENOENT;

	if (readonly)
		return -EROFS;

	return ioop (IOOP_WRITE, mypart, offset, (char *)in /* avoid const warning*/, len);
}

int my_readdir (const char *p, void *buf, fuse_fill_dir_t filler, off_t offset UNUSED, struct fuse_file_info *i UNUSED)
{
	vbprintf ("readdir");
	if (strcmp ("/", p) != 0)
		return -ENOENT;

	dlist *pm = partlist;
	filler (buf, ".", NULL, 0);
	filler (buf, "..", NULL, 0);
	filler (buf, WHOLEPART, NULL, 0);
	while (pm)
	{
		apart *mypart = (apart *)pm->data;
		if (mypart->pn == 0)
			goto tnext;
		char pbuf[PATH_MAX];
		sprintf (pbuf, "Partition%d", mypart->pn);
		filler (buf, pbuf, NULL, 0);
		tnext:
		pm = pm->next;
	}
	return 0;
}

int my_getattr (const char *p, struct stat *stbuf)
{
	memset (stbuf, 0, sizeof (struct stat));
	vbprintf ("getattr: %s", p);
	if (strcmp ("/", p) == 0)
	{
		stbuf->st_mode = S_IFDIR | 0700;
		if (allowall)
			stbuf->st_mode |= 07;
	}
	else if (getpn (p) != -2)
	{
		stbuf->st_mode = S_IFREG | 0600;
			stbuf->st_mode |= 060;
		if (allowall)
			stbuf->st_mode |= 04;
		if (allowallw)
			stbuf->st_mode |= 02;
		stbuf->st_nlink = 1;
		stbuf->st_size = getpart (p)->size;
		stbuf->st_uid = myuid;
		stbuf->st_gid = mygid;
	}
	else
		return -ENOENT;

	return 0;
}

// called when unmounted
void my_destroy (void *u UNUSED)
{
	vbprintf ("destroy");
#ifdef OLDAPI
	VDIDiskCloseImage (vdidisk);
#else
	VDClose (dDisk, 0);
#endif
}

struct fuse_operations oper = {
	.readdir = my_readdir,
	.getattr = my_getattr,
	.access = my_access,
	.open = my_open,
	.read = my_read,
	.write = my_write,
	.destroy = my_destroy,
};

void usage (char *p)
{
		printf (
			"Usage: %s [options] -f image-file mountpoint\n"
			"\t-h help\n"
			"\t-r readonly\n"
#ifndef OLDAPI
			"\t-t specify type (VDI, VMDK, VHD, or raw; default: auto)\n"
#endif
			"\t-a allow all users to read disk\n"
			"\t-aw allow all users to read and write to disk\n"
			"\t-g run in foreground\n"
			"\t-v verbose\n"
			"\t-d debug\n"
		, p);
}

int main (int c, char **v)
{
	char *pname = v[0];

	if (c < 2)
	{
		usage (pname);
		return 255;
	}

#ifndef OLDAPI
	char *diskType = "auto";
#endif
	char *imagefilename = NULL;
	char *mountpoint = NULL;
	int debug = 0;

	while (*v)
	{
		if (strcmp (*v, "--help") == 0 || strcmp (*v, "-h") == 0)
		{
			usage (pname);
			return 0;
		}
		else if (strcmp (*v, "-r") == 0)
			readonly = 1;
		else if (strcmp (*v, "-g") == 0)
			foreground = 1;
		else if (strcmp (*v, "-v") == 0)
			verbose = 1;
		else if (strcmp (*v, "-a") == 0)
			allowall = 1;
		else if (strcmp (*v, "-aw") == 0)
		{
			allowall = 1;
			allowallw = 1;
		}
#ifndef OLDAPI
		else if (strcmp (*v, "-t") == 0)
			diskType = *++v;
#endif
		else if (strcmp (*v, "-f") == 0)
			imagefilename = *++v;
		else if (strcmp (*v, "-d") == 0)
		{
			foreground = 1;
			debug = 1;
		}
		else if (*v[0] == '-')
		{
			errout ("unknown option %s", *v);
			return 255;
		}
		else
			mountpoint = *v;
		v++;
	}

	if (!mountpoint)
	{
		errout ("no mountpoint chosen");
		return 1;
	}

	if (!imagefilename)
	{
		errout ("no image chosen");
		return 1;
	}

#ifndef OLDAPI
	unsigned int di = 0;
	int invalidBackend = 1;
	while (di < (sizeof (validBackends) / sizeof (char *)))
	{
		if (strcmp (validBackends[di], diskType) == 0)
		{
			invalidBackend = 0;
			break;
		}
		di++;
	}
	if (invalidBackend)
	{
		errout ("invalid disk type specified, valid types are VDI, VMDK, VHD, and raw.");
		return 1;
	}
	if (access (imagefilename, F_OK | R_OK | ((!readonly) ? W_OK : 0)) < 0)
	{
		errout ("cannot access disk: %m", errno);
		return 1;
	}
	if (strcmp ("auto", diskType) == 0)
	{
		if (detectDiskType (&diskType, imagefilename) < 0)
			return 1;
	}
#endif

	// open disk
#ifdef OLDAPI
	vdidisk = VDIDiskCreate ();
	int imageopen = VDI_OPEN_FLAGS_NORMAL;
	if (readonly)
		imageopen = VDI_OPEN_FLAGS_READONLY;
	int ret = VDIDiskOpenImage (vdidisk, imagefilename, imageopen);
#else
	dInterface.pszInterfaceName = "What's an interface?";
	dInterface.enmInterface = VDINTERFACETYPE_FIRST;
	VDCreate (&dInterface, &dDisk);
	int imageopen = VD_OPEN_FLAGS_NORMAL;
	if (readonly)
		imageopen = VD_OPEN_FLAGS_READONLY;
	int ret = VDOpen (dDisk, diskType, imagefilename, imageopen, &dInterface);
#endif
	if (VBOX_FAILURE (ret))
	{
		errout ("opening vbox image failed, errno %d", ret);
		return 1;
	}

#ifdef OLDAPI
	disksize = VDIDiskGetSize (vdidisk);
#else
	disksize = VDGetSize (dDisk, 0);
#endif

	apart *rawpart = malloc (sizeof (apart));
	rawpart->pn = 0;
	rawpart->offset = 0;
	rawpart->size = disksize;
	partlist = list_add (partlist, rawpart);

	if (pns () < 0)
		return 1;

	myuid = geteuid ();
	mygid = getegid ();

	struct fuse_args fargs = FUSE_ARGS_INIT (0, NULL);
	fuse_opt_add_arg (&fargs, "");
	fuse_opt_add_arg (&fargs, mountpoint);
	fuse_opt_add_arg (&fargs, "-o");

	if (allowall)
		fuse_opt_add_arg (&fargs, "allow_other");
	else
		fuse_opt_add_arg (&fargs, "allow_root");

	if (foreground)
		fuse_opt_add_arg (&fargs, "-f");
	else
		verbose = 0;

	if (debug)
		fuse_opt_add_arg (&fargs, "-d");
#if FUSE_USE_VERSION >= 26
	return fuse_main (fargs.argc, fargs.argv, &oper, NULL);
#else
	return fuse_main (fargs.argc, fargs.argv, &oper);
#endif
}
Last edited by h2o on 14. Feb 2009, 08:29, edited 1 time in total.
TerryE
Volunteer
Posts: 3572
Joined: 28. May 2008, 08:40
Primary OS: Ubuntu other
VBox Version: PUEL
Guest OSses: Ubuntu 10.04 & 11.10, both Svr&Wstn, Debian, CentOS
Contact:

Post by TerryE »

Thanks, I'll give it a whirl on my AMD64 system tomorrow. //Terry
Read the Forum Posting Guide
Google your Q site:VirtualBox.org or search for the answer before posting.
echo6
Posts: 9
Joined: 13. Oct 2007, 19:07

Post by echo6 »

Tried with the fuse version which compiled fine but I can't mount the vdi image.

Code: Select all

root@toshiba:/home/echo6/.VirtualBox/VDI# /home/echo6/vdifuse -v -r -t VDI -f /home/echo6/.VirtualBox/VDI/disk1.vdi /media/test/
Partition 1:
	Offset: 32256 bytes
	Size: 106896384 bytes

Partition 2:
	Offset: 106928640 bytes
	Size: 1011709440 bytes

Partition 3:
	Offset: 1118638080 bytes
	Size: 4244244480 bytes

fuse: mount failed: Invalid argument

Code: Select all

./vdimount -v -f /home/echo6/.VirtualBox/VDI/disk1.vdi partition-number=raw /media/test
ERROR: invalid partition number 0 or port number 0
Post Reply