[prev in list] [next in list] [prev in thread] [next in thread] 

List:       haiku-commits
Subject:    [haiku-commits] haiku: hrev46458 - in src: add-ons/kernel/drivers/disk/virtual/ram_disk bin bin/pkgm
From:       ingo_weinhold () gmx ! de
Date:       2013-11-30 16:03:08
Message-ID: 20131130160308.22E585C0DB9 () vmrepo ! haiku-os ! org
[Download RAW message or body]

hrev46458 adds 7 changesets to branch 'master'
old head: 0d9151c9ff7935d618ba4718f15136a0e185936b
new head: 25a83d13b95b7ef244ecff48dc1b0e027dcad340
overview: http://cgit.haiku-os.org/haiku/log/?qt=range&q=25a83d1+%5E0d9151c

----------------------------------------------------------------------------

065e6eb: ramdisk: Support file backing
  
  * When registering a ram disk, a file can be specified. Its content will
    be loaded into the ram disk:
      echo register <file> > /dev/disk/virtual/ramdisk/control
  * At any time changes to the ram disk data can be written back to the
    file:
      echo flush /path/to/ramdisk > /dev/disk/virtual/ramdisk/control
    If not explicitly written back, the changes will be lost on reboot.

e26c3df: devfs: Remove superfluous create() hook
  
  We don't support creation of files and the VFS calls open() when an
  entry already exists.

4a5eb16: ramdisk: Add required DEBUG_PAGE_ACCESS_*() macros

28092be: devfs: Add devfs_{get,put}_device()
  
  devfs_get_device() returns the device for a given path (if any), also
  acquiring a reference to its vnode (thus ensuring the device won't go
  away). devfs_put_device() puts the device vnode's reference.

aea2cb9: device_manager: Implement unpublish_device()

5df58b5: Move TextTable from pkgman to libshared

25a83d1: ramdisk: Switch to CLI command for user interface
  
  * Drop the old "echo to control device" interface in favor of an ioctl
    interface.
  * Add CLI program "ramdisk" to manage RAM disks.

                                    [ Ingo Weinhold <ingo_weinhold@gmx.de> ]

----------------------------------------------------------------------------

12 files changed, 1120 insertions(+), 304 deletions(-)
headers/private/file_systems/ram_disk/ram_disk.h |  47 ++
.../private/shared}/TextTable.h                  |   9 +
.../drivers/disk/virtual/ram_disk/ram_disk.cpp   | 744 +++++++++++++------
src/bin/Jamfile                                  |   5 +
src/bin/pkgman/Jamfile                           |   3 +-
src/bin/pkgman/command_search.cpp                |   2 +-
src/bin/ramdisk.cpp                              | 477 ++++++++++++
src/kits/shared/Jamfile                          |   1 +
src/{bin/pkgman => kits/shared}/TextTable.cpp    |   8 +-
src/system/kernel/device_manager/devfs.cpp       |  96 +--
src/system/kernel/device_manager/devfs_private.h |   8 +-
.../kernel/device_manager/device_manager.cpp     |  24 +-

############################################################################

Commit:      065e6eb2f4d661a462bd89130ed3ba6fd9813a45
URL:         http://cgit.haiku-os.org/haiku/commit/?id=065e6eb
Author:      Ingo Weinhold <ingo_weinhold@gmx.de>
Date:        Fri Nov 29 01:07:14 2013 UTC

ramdisk: Support file backing

* When registering a ram disk, a file can be specified. Its content will
  be loaded into the ram disk:
    echo register <file> > /dev/disk/virtual/ramdisk/control
* At any time changes to the ram disk data can be written back to the
  file:
    echo flush /path/to/ramdisk > /dev/disk/virtual/ramdisk/control
  If not explicitly written back, the changes will be lost on reboot.

----------------------------------------------------------------------------

diff --git a/src/add-ons/kernel/drivers/disk/virtual/ram_disk/ram_disk.cpp \
b/src/add-ons/kernel/drivers/disk/virtual/ram_disk/ram_disk.cpp index \
                9437897..c0a9e63 100644
--- a/src/add-ons/kernel/drivers/disk/virtual/ram_disk/ram_disk.cpp
+++ b/src/add-ons/kernel/drivers/disk/virtual/ram_disk/ram_disk.cpp
@@ -100,12 +100,12 @@ struct ControlDevice : Device {
 	{
 	}
 
-	status_t Register(const char* fileName, uint64 deviceSize)
+	status_t Register(const char* filePath, uint64 deviceSize)
 	{
 		device_attr attrs[] = {
 			{B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
 				{string: "RAM Disk Raw Device"}},
-			{kFilePathItem, B_STRING_TYPE, {string: fileName}},
+			{kFilePathItem, B_STRING_TYPE, {string: filePath}},
 			{kDeviceSizeItem, B_UINT64_TYPE, {ui64: deviceSize}},
 			{NULL}
 		};
@@ -130,6 +130,7 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
 		fIndex(-1),
 		fDeviceSize(0),
 		fDeviceName(NULL),
+		fFilePath(NULL),
 		fCache(NULL),
 		fDMAResource(NULL),
 		fIOScheduler(NULL)
@@ -144,14 +145,19 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> \
{  }
 
 		free(fDeviceName);
+		free(fFilePath);
 	}
 
 	int32 Index() const				{ return fIndex; }
 	off_t DeviceSize() const		{ return fDeviceSize; }
 	const char* DeviceName() const	{ return fDeviceName; }
 
-	status_t Init(uint64 deviceSize)
+	status_t Init(const char* filePath, uint64 deviceSize)
 	{
+		fFilePath = filePath != NULL ? strdup(filePath) : NULL;
+		if (filePath != NULL && fFilePath == NULL)
+			return B_NO_MEMORY;
+
 		fDeviceSize = (deviceSize + B_PAGE_SIZE - 1) / B_PAGE_SIZE
 			* B_PAGE_SIZE;
 
@@ -206,6 +212,14 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
 			return error;
 		}
 
+		if (fFilePath != NULL) {
+			error = _LoadFile();
+			if (error != B_OK) {
+				Unprepare();
+				return error;
+			}
+		}
+
 		// no DMA restrictions
 		const dma_restrictions restrictions = {};
 
@@ -254,6 +268,140 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> \
{  }
 	}
 
+	status_t Flush()
+	{
+		static const size_t kPageCountPerIteration = 1024;
+		static const size_t kMaxGapSize = 15;
+
+		int fd = open(fFilePath, O_WRONLY);
+		if (fd < 0)
+			return errno;
+		FileDescriptorCloser fdCloser(fd);
+
+		vm_page** pages = new(std::nothrow) vm_page*[kPageCountPerIteration];
+		ArrayDeleter<vm_page*> pagesDeleter(pages);
+
+		uint8* buffer = (uint8*)malloc(kPageCountPerIteration * B_PAGE_SIZE);
+		MemoryDeleter bufferDeleter(buffer);
+
+		if (pages == NULL || buffer == NULL)
+			return B_NO_MEMORY;
+
+		// Iterate through all pages of the cache and write those back that have
+		// been modified.
+		AutoLocker<VMCache> locker(fCache);
+
+		status_t error = B_OK;
+
+		for (off_t offset = 0; offset < fDeviceSize;) {
+			// find the first modified page at or after the current offset
+			VMCachePagesTree::Iterator it
+				= fCache->pages.GetIterator(offset / B_PAGE_SIZE, true, true);
+			vm_page* firstModified;
+			while ((firstModified = it.Next()) != NULL
+				&& !firstModified->modified) {
+			}
+
+			if (firstModified == NULL)
+				break;
+
+			if (firstModified->busy) {
+				fCache->WaitForPageEvents(firstModified, PAGE_EVENT_NOT_BUSY,
+					true);
+				continue;
+			}
+
+			pages[0] = firstModified;
+			page_num_t firstPageIndex = firstModified->cache_offset;
+			offset = firstPageIndex * B_PAGE_SIZE;
+
+			// Collect more pages until the gap between two modified pages gets
+			// too large or we hit the end of our array.
+			size_t previousModifiedIndex = 0;
+			size_t previousIndex = 0;
+			while (vm_page* page = it.Next()) {
+				page_num_t index = page->cache_offset - firstPageIndex;
+				if (page->busy
+					|| index >= kPageCountPerIteration
+					|| index - previousModifiedIndex > kMaxGapSize) {
+					break;
+				}
+
+				pages[index] = page;
+
+				// clear page array gap since the previous page
+				if (previousIndex + 1 < index) {
+					memset(pages + previousIndex + 1, 0,
+						(index - previousIndex - 1) * sizeof(vm_page*));
+				}
+
+				previousIndex = index;
+				if (page->modified)
+					previousModifiedIndex = index;
+			}
+
+			// mark all pages we want to write busy
+			size_t pagesToWrite = previousModifiedIndex + 1;
+			for (size_t i = 0; i < pagesToWrite; i++) {
+				if (pages[i] != NULL)
+					pages[i]->busy = true;
+			}
+
+			locker.Unlock();
+
+			// copy the pages to our buffer
+			for (size_t i = 0; i < pagesToWrite; i++) {
+				if (vm_page* page = pages[i]) {
+					error = vm_memcpy_from_physical(buffer + i * B_PAGE_SIZE,
+						page->physical_page_number * B_PAGE_SIZE, B_PAGE_SIZE,
+						false);
+					if (error != B_OK) {
+						dprintf("ramdisk: error copying page %" B_PRIu64
+							" data: %s\n", (uint64)page->physical_page_number,
+							strerror(error));
+						break;
+					}
+				} else
+					memset(buffer + i * B_PAGE_SIZE, 0, B_PAGE_SIZE);
+			}
+
+			// write the buffer
+			if (error == B_OK) {
+				ssize_t bytesWritten = pwrite(fd, buffer,
+					pagesToWrite * B_PAGE_SIZE, offset);
+				if (bytesWritten < 0) {
+					dprintf("ramdisk: error writing pages to file: %s\n",
+						strerror(bytesWritten));
+					error = bytesWritten;
+				}
+				else if ((size_t)bytesWritten != pagesToWrite * B_PAGE_SIZE) {
+					dprintf("ramdisk: error writing pages to file: short "
+						"write (%zd/%zu)\n", bytesWritten,
+						pagesToWrite * B_PAGE_SIZE);
+					error = B_ERROR;
+				}
+			}
+
+			// mark the pages unbusy, on success also unmodified
+			locker.Lock();
+
+			for (size_t i = 0; i < pagesToWrite; i++) {
+				if (vm_page* page = pages[i]) {
+					if (error == B_OK)
+						page->modified = false;
+					fCache->MarkPageUnbusy(page);
+				}
+			}
+
+			if (error != B_OK)
+				break;
+
+			offset += pagesToWrite * B_PAGE_SIZE;
+		}
+
+		return error;
+	}
+
 	status_t DoIO(IORequest* request)
 	{
 		return fIOScheduler->ScheduleRequest(request);
@@ -296,6 +444,9 @@ private:
 		while (length > 0) {
 			vm_page* page = pages[index];
 
+			if (isWrite)
+				page->modified = true;
+
 			error = _CopyData(page, vecs, vecOffset, isWrite);
 			if (error != B_OK)
 				break;
@@ -412,6 +563,7 @@ private:
 			pageData = (uint8*)virtualAddress;
 		}
 
+		status_t error = B_OK;
 		size_t length = B_PAGE_SIZE;
 		while (length > 0) {
 			size_t toCopy = std::min((generic_size_t)length,
@@ -425,13 +577,13 @@ private:
 
 			phys_addr_t vecAddress = vecs->base + vecOffset;
 
-			status_t error = toPage
+			error = toPage
 				? vm_memcpy_from_physical(pageData, vecAddress, toCopy, false)
 				: (page != NULL
 					? vm_memcpy_to_physical(vecAddress, pageData, toCopy, false)
 					: vm_memset_physical(vecAddress, 0, toCopy));
 			if (error != B_OK)
-				return error;
+				break;
 
 			pageData += toCopy;
 			length -= toCopy;
@@ -443,13 +595,132 @@ private:
 			thread_unpin_from_current_cpu(thread);
 		}
 
-		return B_OK;
+		return error;
+	}
+
+	status_t _LoadFile()
+	{
+		static const size_t kPageCountPerIteration = 1024;
+
+		int fd = open(fFilePath, O_RDONLY);
+		if (fd < 0)
+			return errno;
+		FileDescriptorCloser fdCloser(fd);
+
+		vm_page** pages = new(std::nothrow) vm_page*[kPageCountPerIteration];
+		ArrayDeleter<vm_page*> pagesDeleter(pages);
+
+		uint8* buffer = (uint8*)malloc(kPageCountPerIteration * B_PAGE_SIZE);
+		MemoryDeleter bufferDeleter(buffer);
+			// TODO: Ideally we wouldn't use a buffer to read the file content,
+			// but read into the pages we allocated directly. Unfortunately
+			// there's no API to do that yet.
+
+		if (pages == NULL || buffer == NULL)
+			return B_NO_MEMORY;
+
+		status_t error = B_OK;
+
+		page_num_t allocatedPages = 0;
+		off_t offset = 0;
+		off_t sizeRemaining = fDeviceSize;
+		while (sizeRemaining > 0) {
+			// Note: fDeviceSize is B_PAGE_SIZE aligned.
+			size_t pagesToRead = std::min(kPageCountPerIteration,
+				size_t(sizeRemaining / B_PAGE_SIZE));
+
+			// allocate the missing pages
+			if (allocatedPages < pagesToRead) {
+				vm_page_reservation reservation;
+				vm_page_reserve_pages(&reservation,
+					pagesToRead - allocatedPages, VM_PRIORITY_SYSTEM);
+
+				while (allocatedPages < pagesToRead) {
+					pages[allocatedPages++]
+						= vm_page_allocate_page(&reservation, PAGE_STATE_WIRED);
+				}
+
+				vm_page_unreserve_pages(&reservation);
+			}
+
+			// read from the file
+			size_t bytesToRead = pagesToRead * B_PAGE_SIZE;
+			ssize_t bytesRead = pread(fd, buffer, bytesToRead, offset);
+			if (bytesRead < 0) {
+				error = bytesRead;
+				break;
+			}
+			size_t pagesRead = (bytesRead + B_PAGE_SIZE - 1) / B_PAGE_SIZE;
+			if (pagesRead < pagesToRead) {
+				error = B_ERROR;
+				break;
+			}
+
+			// clear the last read page, if partial
+			if ((size_t)bytesRead < pagesRead * B_PAGE_SIZE) {
+				memset(buffer + bytesRead, 0,
+					pagesRead * B_PAGE_SIZE - bytesRead);
+			}
+
+			// copy data to allocated pages
+			for (size_t i = 0; i < pagesRead; i++) {
+				vm_page* page = pages[i];
+				error = vm_memcpy_to_physical(
+					page->physical_page_number * B_PAGE_SIZE,
+					buffer + i * B_PAGE_SIZE, B_PAGE_SIZE, false);
+				if (error != B_OK)
+					break;
+			}
+
+			if (error != B_OK)
+				break;
+
+			// Add pages to cache. Ignore clear pages, though. Move those to the
+			// beginning of the array, so we can reuse them in the next
+			// iteration.
+			AutoLocker<VMCache> locker(fCache);
+
+			size_t clearPages = 0;
+			for (size_t i = 0; i < pagesRead; i++) {
+				uint64* pageData = (uint64*)(buffer + i * B_PAGE_SIZE);
+				bool isClear = true;
+				for (size_t k = 0; isClear && k < B_PAGE_SIZE / 8; k++)
+					isClear = pageData[k] == 0;
+
+				if (isClear)
+					pages[clearPages++] = pages[i];
+				else
+					fCache->InsertPage(pages[i], offset + i * B_PAGE_SIZE);
+			}
+
+			locker.Unlock();
+
+			// Move any left-over allocated pages to the end of the empty pages
+			// and compute the new allocated pages count.
+			if (pagesRead < allocatedPages) {
+				size_t count = allocatedPages - pagesRead;
+				memcpy(pages + clearPages, pages + pagesRead,
+					count * sizeof(vm_page*));
+				clearPages += count;
+			}
+			allocatedPages = clearPages;
+
+			offset += pagesRead * B_PAGE_SIZE;
+			sizeRemaining -= pagesRead * B_PAGE_SIZE;
+		}
+
+		// free left-over allocated pages
+		for (size_t i = 0; i < allocatedPages; i++)
+			vm_page_free(NULL, pages[i]);
+
+		return error;
 	}
 
 private:
 	int32			fIndex;
 	off_t			fDeviceSize;
 	char*			fDeviceName;
+	char*			fFilePath;
 	VMCache*		fCache;
 	DMAResource*	fDMAResource;
 	IOScheduler*	fIOScheduler;
@@ -557,6 +828,22 @@ parse_command_line(char* buffer, char**& _argv, int& _argc)
 }
 
 
+static RawDevice*
+find_raw_device(const char* deviceName)
+{
+	if (strncmp(deviceName, "/dev/", 5) == 0)
+		deviceName += 5;
+
+	for (RawDeviceList::Iterator it = sDeviceList.GetIterator();
+			RawDevice* device = it.Next();) {
+		if (strcmp(device->DeviceName(), deviceName) == 0)
+			return device;
+	}
+
+	return NULL;
+}
+
+
 //	#pragma mark - driver
 
 
@@ -591,18 +878,17 @@ ram_disk_driver_register_device(device_node* parent)
 static status_t
 ram_disk_driver_init_driver(device_node* node, void** _driverCookie)
 {
-//	const char* fileName;
 	uint64 deviceSize;
-// 	if (sDeviceManager->get_attr_string(node, kFilePathItem, &fileName, false)
-// 			== B_OK) {
 	if (sDeviceManager->get_attr_uint64(node, kDeviceSizeItem, &deviceSize,
 			false) == B_OK) {
+		const char* filePath = NULL;
+		sDeviceManager->get_attr_string(node, kFilePathItem, &filePath, false);
+
 		RawDevice* device = new(std::nothrow) RawDevice(node);
 		if (device == NULL)
 			return B_NO_MEMORY;
 
-//		status_t error = device->Init(fileName);
-		status_t error = device->Init(deviceSize);
+		status_t error = device->Init(filePath, deviceSize);
 		if (error != B_OK) {
 			delete device;
 			return error;
@@ -686,6 +972,7 @@ ram_disk_control_device_read(void* cookie, off_t position, void* \
buffer,  }
 
 
+
 static status_t
 ram_disk_control_device_write(void* cookie, off_t position, const void* data,
 	size_t* _length)
@@ -724,38 +1011,68 @@ ram_disk_control_device_write(void* cookie, off_t position, \
const void* data,  // execute command
 	if (strcmp(argv[0], "help") == 0) {
 		// help
-// 		dprintf("register <path>\n");
-// 		dprintf("  Registers file <path> as a new ram disk device.\n");
-		dprintf("register <size>\n");
-		dprintf("  Registers a new ram disk device with size <size>.\n");
+		dprintf("register (<path> | -s <size>)\n");
+		dprintf("  Registers a new ram disk device backed by file <path> or\n"
+			"  an unbacked ram disk device with size <size>.\n");
 		dprintf("unregister <device>\n");
 		dprintf("  Unregisters <device>.\n");
+		dprintf("flush <device>\n");
+		dprintf("  Writes <device> changes back to the file associated with\n"
+			"  it, if any.\n");
 	} else if (strcmp(argv[0], "register") == 0) {
 		// register
-		if (argc != 2) {
-			dprintf("Usage: register <size>\n");
+		if (argc < 2 || argc > 3 || (argc == 3 && strcmp(argv[1], "-s") != 0)) {
+			dprintf("Usage: register (<path> | -s <size>)\n");
 			return B_BAD_VALUE;
 		}
 
-		// parse size argument
-		char* end;
-		uint64 deviceSize = strtoll(argv[1], &end, 0);
-		if (end == argv[1]) {
-			dprintf("Invalid size argument: \"%s\"\n", argv[1]);
-			return B_BAD_VALUE;
-		}
+		KPath path;
+		uint64 deviceSize = 0;
 
-		switch (*end) {
-			case 'g':
-				deviceSize *= 1024;
-			case 'm':
-				deviceSize *= 1024;
-			case 'k':
-				deviceSize *= 1024;
-				break;
+		if (argc == 2) {
+			// get a normalized file path
+			status_t error = path.SetTo(argv[1], true);
+			if (error != B_OK) {
+				dprintf("Invalid path \"%s\": %s\n", argv[1], strerror(error));
+				return B_BAD_VALUE;
+			}
+
+			struct stat st;
+			if (lstat(path.Path(), &st) != 0) {
+				dprintf("Failed to stat \"%s\": %s\n", path.Path(),
+					strerror(errno));
+				return errno;
+			}
+
+			if (!S_ISREG(st.st_mode)) {
+				dprintf("\"%s\" is not a file!\n", path.Path());
+				return B_BAD_VALUE;
+			}
+
+			deviceSize = st.st_size;
+		} else {
+			// parse size argument
+			const char* sizeString = argv[2];
+			char* end;
+			deviceSize = strtoll(sizeString, &end, 0);
+			if (end == sizeString) {
+				dprintf("Invalid size argument: \"%s\"\n", sizeString);
+				return B_BAD_VALUE;
+			}
+
+			switch (*end) {
+				case 'g':
+					deviceSize *= 1024;
+				case 'm':
+					deviceSize *= 1024;
+				case 'k':
+					deviceSize *= 1024;
+					break;
+			}
 		}
 
-		return device->Register(NULL, deviceSize);
+		return device->Register(path.Length() > 0 ? path.Path() : NULL,
+			deviceSize);
 	} else if (strcmp(argv[0], "unregister") == 0) {
 		// unregister
 		if (argc != 2) {
@@ -764,41 +1081,65 @@ ram_disk_control_device_write(void* cookie, off_t position, \
const void* data,  }
 
 		const char* deviceName = argv[1];
-		if (strncmp(deviceName, "/dev/", 5) == 0)
-			deviceName += 5;
 
 		// find the device in the list and unregister it
 		MutexLocker locker(sDeviceListLock);
-		for (RawDeviceList::Iterator it = sDeviceList.GetIterator();
-				RawDevice* device = it.Next();) {
-			if (strcmp(device->DeviceName(), deviceName) == 0) {
-				// TODO: Race condition: We should mark the device as going to
-				// be unregistered, so no one else can try the same after we
-				// unlock!
-				locker.Unlock();
+		if (RawDevice* device = find_raw_device(deviceName)) {
+			// TODO: Race condition: We should mark the device as going to
+			// be unregistered, so no one else can try the same after we
+			// unlock!
+			locker.Unlock();
 // TODO: The following doesn't work! unpublish_device(), as per implementation
 // (partially commented out) and unregister_node() returns B_BUSY.
-				status_t error = sDeviceManager->unpublish_device(
-					device->Node(), device->DeviceName());
-				if (error != B_OK) {
-					dprintf("Failed to unpublish device \"%s\": %s\n",
-						deviceName, strerror(error));
-					return error;
-				}
-
-				error = sDeviceManager->unregister_node(device->Node());
-				if (error != B_OK) {
-					dprintf("Failed to unregister node \"%s\": %s\n",
-						deviceName, strerror(error));
-					return error;
-				}
+			status_t error = sDeviceManager->unpublish_device(
+				device->Node(), device->DeviceName());
+			if (error != B_OK) {
+				dprintf("Failed to unpublish device \"%s\": %s\n",
+					deviceName, strerror(error));
+				return error;
+			}
 
-				return B_OK;
+			error = sDeviceManager->unregister_node(device->Node());
+			if (error != B_OK) {
+				dprintf("Failed to unregister node \"%s\": %s\n",
+					deviceName, strerror(error));
+				return error;
 			}
+
+			return B_OK;
 		}
 
 		dprintf("Device \"%s\" not found!\n", deviceName);
 		return B_BAD_VALUE;
+	} else if (strcmp(argv[0], "flush") == 0) {
+		// flush
+		if (argc != 2) {
+			dprintf("Usage: unregister <device>\n");
+			return B_BAD_VALUE;
+		}
+
+		const char* deviceName = argv[1];
+
+		// find the device in the list and flush it
+		MutexLocker locker(sDeviceListLock);
+		RawDevice* device = find_raw_device(deviceName);
+		if (device == NULL) {
+			dprintf("Device \"%s\" not found!\n", deviceName);
+			return B_BAD_VALUE;
+		}
+
+		// TODO: Race condition: Once we unlock someone could unregister the
+		// device. We should probably open the device by path, and use a
+		// special ioctl.
+		locker.Unlock();
+
+		status_t error = device->Flush();
+		if (error != B_OK) {
+			dprintf("Failed to flush device: %s\n", strerror(error));
+			return error;
+		}
+
+		return B_OK;
 	} else {
 		dprintf("Invalid command \"%s\"!\n", argv[0]);
 		return B_BAD_VALUE;

############################################################################

Commit:      e26c3df48cc8599688803a11f35535ec3f89c6b6
URL:         http://cgit.haiku-os.org/haiku/commit/?id=e26c3df
Author:      Ingo Weinhold <ingo_weinhold@gmx.de>
Date:        Fri Nov 29 15:05:56 2013 UTC

devfs: Remove superfluous create() hook

We don't support creation of files and the VFS calls open() when an
entry already exists.

----------------------------------------------------------------------------

diff --git a/src/system/kernel/device_manager/devfs.cpp \
b/src/system/kernel/device_manager/devfs.cpp index ca77751..6127269 100644
--- a/src/system/kernel/device_manager/devfs.cpp
+++ b/src/system/kernel/device_manager/devfs.cpp
@@ -1082,70 +1082,6 @@ devfs_remove_vnode(fs_volume* _volume, fs_vnode* _v, bool \
reenter)  
 
 static status_t
-devfs_create(fs_volume* _volume, fs_vnode* _dir, const char* name, int openMode,
-	int perms, void** _cookie, ino_t* _newVnodeID)
-{
-	struct devfs_vnode* dir = (struct devfs_vnode*)_dir->private_node;
-	struct devfs* fs = (struct devfs*)_volume->private_volume;
-	struct devfs_cookie* cookie;
-	struct devfs_vnode* vnode;
-	status_t status = B_OK;
-
-	TRACE(("devfs_create: dir %p, name \"%s\", openMode 0x%x, fs_cookie %p \n",
-		dir, name, openMode, _cookie));
-
-	RecursiveLocker locker(fs->lock);
-
-	// look it up
-	vnode = devfs_find_in_dir(dir, name);
-	if (vnode == NULL)
-		return EROFS;
-
-	if ((openMode & O_EXCL) != 0)
-		return B_FILE_EXISTS;
-
-	status = get_vnode(fs->volume, vnode->id, NULL);
-	if (status != B_OK)
-		return status;
-
-	locker.Unlock();
-
-	*_newVnodeID = vnode->id;
-
-	cookie = (struct devfs_cookie*)malloc(sizeof(struct devfs_cookie));
-	if (cookie == NULL) {
-		status = B_NO_MEMORY;
-		goto err1;
-	}
-
-	if (S_ISCHR(vnode->stream.type)) {
-		BaseDevice* device = vnode->stream.u.dev.device;
-		status = device->InitDevice();
-		if (status != B_OK)
-			goto err2;
-
-		char path[B_FILE_NAME_LENGTH];
-		get_device_name(vnode, path, sizeof(path));
-
-		status = device->Open(path, openMode, &cookie->device_cookie);
-		if (status != B_OK) {
-			device->UninitDevice();
-			goto err2;
-		}
-	}
-
-	*_cookie = cookie;
-	return B_OK;
-
-err2:
-	free(cookie);
-err1:
-	put_vnode(fs->volume, vnode->id);
-	return status;
-}
-
-
-static status_t
 devfs_open(fs_volume* _volume, fs_vnode* _vnode, int openMode,
 	void** _cookie)
 {
@@ -1978,7 +1914,7 @@ fs_vnode_ops kVnodeOps = {
 	NULL,
 
 	/* file */
-	&devfs_create,
+	NULL,	// create
 	&devfs_open,
 	&devfs_close,
 	&devfs_free_cookie,

############################################################################

Commit:      4a5eb160530cec0e87772e2aa3aa1a7f1703b55e
URL:         http://cgit.haiku-os.org/haiku/commit/?id=4a5eb16
Author:      Ingo Weinhold <ingo_weinhold@gmx.de>
Date:        Fri Nov 29 23:24:42 2013 UTC

ramdisk: Add required DEBUG_PAGE_ACCESS_*() macros

----------------------------------------------------------------------------

diff --git a/src/add-ons/kernel/drivers/disk/virtual/ram_disk/ram_disk.cpp \
b/src/add-ons/kernel/drivers/disk/virtual/ram_disk/ram_disk.cpp index \
                c0a9e63..529b61b 100644
--- a/src/add-ons/kernel/drivers/disk/virtual/ram_disk/ram_disk.cpp
+++ b/src/add-ons/kernel/drivers/disk/virtual/ram_disk/ram_disk.cpp
@@ -343,8 +343,10 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
 			// mark all pages we want to write busy
 			size_t pagesToWrite = previousModifiedIndex + 1;
 			for (size_t i = 0; i < pagesToWrite; i++) {
-				if (pages[i] != NULL)
+				if (vm_page* page = pages[i]) {
+					DEBUG_PAGE_ACCESS_START(page);
 					pages[i]->busy = true;
+				}
 			}
 
 			locker.Unlock();
@@ -390,6 +392,7 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
 					if (error == B_OK)
 						page->modified = false;
 					fCache->MarkPageUnbusy(page);
+					DEBUG_PAGE_ACCESS_END(page);
 				}
 			}
 
@@ -485,6 +488,7 @@ private:
 					continue;
 				}
 
+				DEBUG_PAGE_ACCESS_START(page);
 				page->busy = true;
 			} else
 				missingPages++;
@@ -531,10 +535,13 @@ private:
 					if (success) {
 						fCache->InsertPage(page, offset);
 						fCache->MarkPageUnbusy(page);
+						DEBUG_PAGE_ACCESS_END(page);
 					} else
 						vm_page_free(NULL, page);
-				} else
+				} else {
 					fCache->MarkPageUnbusy(page);
+					DEBUG_PAGE_ACCESS_END(page);
+				}
 			}
 
 			offset += B_PAGE_SIZE;
@@ -687,10 +694,12 @@ private:
 				for (size_t k = 0; isClear && k < B_PAGE_SIZE / 8; k++)
 					isClear = pageData[k] == 0;
 
-				if (isClear)
+				if (isClear) {
 					pages[clearPages++] = pages[i];
-				else
+				} else {
 					fCache->InsertPage(pages[i], offset + i * B_PAGE_SIZE);
+					DEBUG_PAGE_ACCESS_END(pages[i]);
+				}
 			}
 
 			locker.Unlock();

############################################################################

Commit:      28092be1969370f0c4a92536ed066c5700cc56f0
URL:         http://cgit.haiku-os.org/haiku/commit/?id=28092be
Author:      Ingo Weinhold <ingo_weinhold@gmx.de>
Date:        Fri Nov 29 23:26:43 2013 UTC

devfs: Add devfs_{get,put}_device()

devfs_get_device() returns the device for a given path (if any), also
acquiring a reference to its vnode (thus ensuring the device won't go
away). devfs_put_device() puts the device vnode's reference.

----------------------------------------------------------------------------

diff --git a/src/system/kernel/device_manager/devfs.cpp \
b/src/system/kernel/device_manager/devfs.cpp index 6127269..88564a9 100644
--- a/src/system/kernel/device_manager/devfs.cpp
+++ b/src/system/kernel/device_manager/devfs.cpp
@@ -2143,6 +2143,34 @@ devfs_unpublish_device(BaseDevice* device, bool disconnect)
 }
 
 
+/*!	Gets the device for a given devfs relative path.
+	If successful the call must be balanced with a call to devfs_put_device().
+*/
+status_t
+devfs_get_device(const char* path, BaseDevice*& _device)
+{
+	devfs_vnode* node;
+	status_t status = get_node_for_path(sDeviceFileSystem, path, &node);
+	if (status != B_OK)
+		return status;
+
+	if (!S_ISCHR(node->stream.type) || node->stream.u.dev.partition != NULL) {
+		put_vnode(sDeviceFileSystem->volume, node->id);
+		return B_BAD_VALUE;
+	}
+
+	_device = node->stream.u.dev.device;
+	return B_OK;
+}
+
+
+void
+devfs_put_device(BaseDevice* device)
+{
+	put_vnode(sDeviceFileSystem->volume, device->ID());
+}
+
+
 void
 devfs_compute_geometry_size(device_geometry* geometry, uint64 blockCount,
 	uint32 blockSize)
@@ -2175,5 +2203,3 @@ devfs_publish_device(const char* path, device_hooks* hooks)
 {
 	return legacy_driver_publish(path, hooks);
 }
-
-
diff --git a/src/system/kernel/device_manager/devfs_private.h \
b/src/system/kernel/device_manager/devfs_private.h index dc0ea91..7436513 100644
--- a/src/system/kernel/device_manager/devfs_private.h
+++ b/src/system/kernel/device_manager/devfs_private.h
@@ -16,8 +16,10 @@ namespace BPrivate {
 using BPrivate::BaseDevice;
 
 
-status_t devfs_publish_device(const char* path, BaseDevice* device);
-status_t devfs_unpublish_device(BaseDevice* device, bool disconnect);
-status_t devfs_get_device(dev_t device, ino_t node, BaseDevice** _device);
+status_t	devfs_publish_device(const char* path, BaseDevice* device);
+status_t	devfs_unpublish_device(BaseDevice* device, bool disconnect);
+
+status_t	devfs_get_device(const char* path, BaseDevice*& _device);
+void		devfs_put_device(BaseDevice* device);
 
 #endif	/* DEVFS_PRIVATE_H */

############################################################################

Commit:      aea2cb917eb329bf2b56b64097e113d4417ba3b1
URL:         http://cgit.haiku-os.org/haiku/commit/?id=aea2cb9
Author:      Ingo Weinhold <ingo_weinhold@gmx.de>
Date:        Fri Nov 29 23:27:30 2013 UTC

device_manager: Implement unpublish_device()

----------------------------------------------------------------------------

diff --git a/src/system/kernel/device_manager/device_manager.cpp \
b/src/system/kernel/device_manager/device_manager.cpp index 5de0b31..5537aa0 100644
--- a/src/system/kernel/device_manager/device_manager.cpp
+++ b/src/system/kernel/device_manager/device_manager.cpp
@@ -757,21 +757,17 @@ unpublish_device(device_node *node, const char *path)
 	if (path == NULL)
 		return B_BAD_VALUE;
 
-	RecursiveLocker _(sLock);
-
-#if 0
-	DeviceList::ConstIterator iterator = node->Devices().GetIterator();
-	while (iterator.HasNext()) {
-		Device* device = iterator.Next();
-		if (!strcmp(device->Path(), path)) {
-			node->RemoveDevice(device);
-			delete device;
-			return B_OK;
-		}
-	}
-#endif
+	BaseDevice* baseDevice;
+	status_t error = devfs_get_device(path, baseDevice);
+	if (error != B_OK)
+		return error;
+	CObjectDeleter<BaseDevice> baseDevicePutter(baseDevice, &devfs_put_device);
+
+	Device* device = dynamic_cast<Device*>(baseDevice);
+	if (device == NULL || device->Node() != node)
+		return B_BAD_VALUE;
 
-	return B_ENTRY_NOT_FOUND;
+	return devfs_unpublish_device(device, true);
 }
 
 

############################################################################

Commit:      5df58b522b52d5914bf8b49fc47ff46fde56877f
URL:         http://cgit.haiku-os.org/haiku/commit/?id=5df58b5
Author:      Ingo Weinhold <ingo_weinhold@gmx.de>
Date:        Sat Nov 30 15:18:03 2013 UTC

Move TextTable from pkgman to libshared

----------------------------------------------------------------------------

diff --git a/src/bin/pkgman/TextTable.h b/headers/private/shared/TextTable.h
similarity index 91%
rename from src/bin/pkgman/TextTable.h
rename to headers/private/shared/TextTable.h
index dd94bfe..3d37e38 100644
--- a/src/bin/pkgman/TextTable.h
+++ b/headers/private/shared/TextTable.h
@@ -11,6 +11,9 @@
 #include <StringList.h>
 
 
+namespace BPrivate {
+
+
 class TextTable {
 public:
 								TextTable();
@@ -39,4 +42,10 @@ private:
 };
 
 
+} // namespace BPrivate
+
+
+using ::BPrivate::TextTable;
+
+
 #endif	// TEXT_TABLE_H
diff --git a/src/bin/pkgman/Jamfile b/src/bin/pkgman/Jamfile
index e225036..3ff4b48 100644
--- a/src/bin/pkgman/Jamfile
+++ b/src/bin/pkgman/Jamfile
@@ -17,8 +17,7 @@ BinCommand pkgman :
 	JobStateListener.cpp
 	PackageManager.cpp
 	pkgman.cpp
-	TextTable.cpp
 	:
-	package be
+	libshared.a package be
 	$(TARGET_LIBSUPC++) $(TARGET_LIBSTDC++)
 ;
diff --git a/src/bin/pkgman/command_search.cpp b/src/bin/pkgman/command_search.cpp
index d84cff1..23c1ce2 100644
--- a/src/bin/pkgman/command_search.cpp
+++ b/src/bin/pkgman/command_search.cpp
@@ -18,11 +18,11 @@
 #include <set>
 
 #include <package/solver/SolverPackage.h>
+#include <TextTable.h>
 
 #include "Command.h"
 #include "PackageManager.h"
 #include "pkgman.h"
-#include "TextTable.h"
 
 
 // TODO: internationalization!
diff --git a/src/kits/shared/Jamfile b/src/kits/shared/Jamfile
index e170392..6a44e76 100644
--- a/src/kits/shared/Jamfile
+++ b/src/kits/shared/Jamfile
@@ -45,6 +45,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
 			ShakeTrackingFilter.cpp
 			StringForRate.cpp
 			StringForSize.cpp
+			TextTable.cpp
 			Thread.cpp
 			Variant.cpp
 			;
diff --git a/src/bin/pkgman/TextTable.cpp b/src/kits/shared/TextTable.cpp
similarity index 98%
rename from src/bin/pkgman/TextTable.cpp
rename to src/kits/shared/TextTable.cpp
index f173665..2601205 100644
--- a/src/bin/pkgman/TextTable.cpp
+++ b/src/kits/shared/TextTable.cpp
@@ -4,13 +4,16 @@
  */
 
 
-#include "TextTable.h"
+#include <TextTable.h>
 
 #include <stdio.h>
 
 #include <algorithm>
 
 
+namespace BPrivate {
+
+
 // #pragma mark - Column
 
 
@@ -272,3 +275,6 @@ TextTable::Print(int32 maxWidth)
 		fputs(line.String(), stdout);
 	}
 }
+
+
+} // namespace BPrivate

############################################################################

Revision:    hrev46458
Commit:      25a83d13b95b7ef244ecff48dc1b0e027dcad340
URL:         http://cgit.haiku-os.org/haiku/commit/?id=25a83d1
Author:      Ingo Weinhold <ingo_weinhold@gmx.de>
Date:        Sat Nov 30 16:01:49 2013 UTC

ramdisk: Switch to CLI command for user interface

* Drop the old "echo to control device" interface in favor of an ioctl
  interface.
* Add CLI program "ramdisk" to manage RAM disks.

----------------------------------------------------------------------------

diff --git a/headers/private/file_systems/ram_disk/ram_disk.h \
b/headers/private/file_systems/ram_disk/ram_disk.h new file mode 100644
index 0000000..0a8279a
--- /dev/null
+++ b/headers/private/file_systems/ram_disk/ram_disk.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
+ * Distributed under the terms of the MIT License.
+ */
+#ifndef _PRIVATE_FILE_SYSTEMS_RAM_DISK_RAM_DISK_H
+#define _PRIVATE_FILE_SYSTEMS_RAM_DISK_RAM_DISK_H
+
+
+#include <Drivers.h>
+#include <StorageDefs.h>
+
+
+#define RAM_DISK_CONTROL_DEVICE_NAME	"disk/virtual/ram/control"
+#define RAM_DISK_RAW_DEVICE_BASE_NAME	"disk/virtual/ram"
+
+
+enum {
+	RAM_DISK_IOCTL_REGISTER		= B_DEVICE_OP_CODES_END + 1,
+	RAM_DISK_IOCTL_UNREGISTER,
+	RAM_DISK_IOCTL_FLUSH,
+	RAM_DISK_IOCTL_INFO
+};
+
+
+struct ram_disk_ioctl_register {
+	uint64	size;
+	char	path[B_PATH_NAME_LENGTH];
+
+	// return value
+	int32	id;
+};
+
+
+struct ram_disk_ioctl_unregister {
+	int32	id;
+};
+
+
+struct ram_disk_ioctl_info {
+	// return values
+	int32	id;
+	uint64	size;
+	char	path[B_PATH_NAME_LENGTH];
+};
+
+
+#endif	// _PRIVATE_FILE_SYSTEMS_RAM_DISK_RAM_DISK_H
diff --git a/src/add-ons/kernel/drivers/disk/virtual/ram_disk/ram_disk.cpp \
b/src/add-ons/kernel/drivers/disk/virtual/ram_disk/ram_disk.cpp index \
                529b61b..8807d27 100644
--- a/src/add-ons/kernel/drivers/disk/virtual/ram_disk/ram_disk.cpp
+++ b/src/add-ons/kernel/drivers/disk/virtual/ram_disk/ram_disk.cpp
@@ -4,6 +4,8 @@
  */
 
 
+#include <file_systems/ram_disk/ram_disk.h>
+
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -50,12 +52,12 @@ static const char* const kControlDeviceModuleName
 static const char* const kRawDeviceModuleName
 	= "drivers/disk/virtual/ram_disk/raw/device_v1";
 
-static const char* const kControlDeviceName
-	= "disk/virtual/ram/control";
-static const char* const kRawDeviceBaseName = "disk/virtual/ram";
+static const char* const kControlDeviceName = RAM_DISK_CONTROL_DEVICE_NAME;
+static const char* const kRawDeviceBaseName = RAM_DISK_RAW_DEVICE_BASE_NAME;
 
 static const char* const kFilePathItem = "ram_disk/file_path";
 static const char* const kDeviceSizeItem = "ram_disk/device_size";
+static const char* const kDeviceIDItem = "ram_disk/id";
 
 
 struct RawDevice;
@@ -65,6 +67,11 @@ struct device_manager_info* sDeviceManager;
 
 static RawDeviceList sDeviceList;
 static mutex sDeviceListLock = MUTEX_INITIALIZER("ram disk device list");
+static uint64 sUsedRawDeviceIDs = 0;
+
+
+static int32	allocate_raw_device_id();
+static void		free_raw_device_id(int32 id);
 
 
 struct Device {
@@ -100,19 +107,37 @@ struct ControlDevice : Device {
 	{
 	}
 
-	status_t Register(const char* filePath, uint64 deviceSize)
+	status_t Register(const char* filePath, uint64 deviceSize, int32& _id)
 	{
+		int32 id = allocate_raw_device_id();
+		if (id < 0)
+			return B_BUSY;
+
 		device_attr attrs[] = {
 			{B_DEVICE_PRETTY_NAME, B_STRING_TYPE,
 				{string: "RAM Disk Raw Device"}},
-			{kFilePathItem, B_STRING_TYPE, {string: filePath}},
 			{kDeviceSizeItem, B_UINT64_TYPE, {ui64: deviceSize}},
+			{kDeviceIDItem, B_UINT32_TYPE, {ui32: (uint32)id}},
+			{kFilePathItem, B_STRING_TYPE, {string: filePath}},
 			{NULL}
 		};
 
-		return sDeviceManager->register_node(
+		// If filePath is NULL, remove the attribute.
+		if (filePath == NULL) {
+			size_t count = sizeof(attrs) / sizeof(attrs[0]);
+			memset(attrs + count - 2, 0, sizeof(attrs[0]));
+		}
+
+		status_t error = sDeviceManager->register_node(
 			sDeviceManager->get_parent_node(Node()), kDriverModuleName, attrs,
 			NULL, NULL);
+		if (error != B_OK) {
+			free_raw_device_id(id);
+			return error;
+		}
+
+		_id = id;
+		return B_OK;
 	}
 
 	virtual status_t PublishDevice()
@@ -127,7 +152,8 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
 	RawDevice(device_node* node)
 		:
 		Device(node),
-		fIndex(-1),
+		fID(-1),
+		fUnregistered(false),
 		fDeviceSize(0),
 		fDeviceName(NULL),
 		fFilePath(NULL),
@@ -139,7 +165,7 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
 
 	virtual ~RawDevice()
 	{
-		if (fIndex >= 0) {
+		if (fID >= 0) {
 			MutexLocker locker(sDeviceListLock);
 			sDeviceList.Remove(this);
 		}
@@ -148,12 +174,20 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> \
{  free(fFilePath);
 	}
 
-	int32 Index() const				{ return fIndex; }
+	int32 ID() const				{ return fID; }
 	off_t DeviceSize() const		{ return fDeviceSize; }
 	const char* DeviceName() const	{ return fDeviceName; }
 
-	status_t Init(const char* filePath, uint64 deviceSize)
+	bool IsUnregistered() const		{ return fUnregistered; }
+
+	void SetUnregistered(bool unregistered)
+	{
+		fUnregistered = unregistered;
+	}
+
+	status_t Init(int32 id, const char* filePath, uint64 deviceSize)
 	{
+		fID = id;
 		fFilePath = filePath != NULL ? strdup(filePath) : NULL;
 		if (filePath != NULL && fFilePath == NULL)
 			return B_NO_MEMORY;
@@ -167,23 +201,10 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> \
{  return B_BAD_VALUE;
 		}
 
-		// find a free slot
-		fIndex = 0;
-		RawDevice* nextDevice = NULL;
-		MutexLocker locker(sDeviceListLock);
-		for (RawDeviceList::Iterator it = sDeviceList.GetIterator();
-				(nextDevice = it.Next()) != NULL;) {
-			if (nextDevice->Index() > fIndex)
-				break;
-			fIndex = nextDevice->Index() + 1;
-		}
-
-		sDeviceList.InsertBefore(nextDevice, this);
-
 		// construct our device path
 		KPath path(kRawDeviceBaseName);
 		char buffer[32];
-		snprintf(buffer, sizeof(buffer), "%" B_PRId32 "/raw", fIndex);
+		snprintf(buffer, sizeof(buffer), "%" B_PRId32 "/raw", fID);
 
 		status_t error = path.Append(buffer);
 		if (error != B_OK)
@@ -191,6 +212,17 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
 
 		fDeviceName = path.DetachBuffer();
 
+		// insert into device list
+		RawDevice* nextDevice = NULL;
+		MutexLocker locker(sDeviceListLock);
+		for (RawDeviceList::Iterator it = sDeviceList.GetIterator();
+				(nextDevice = it.Next()) != NULL;) {
+			if (nextDevice->ID() > fID)
+				break;
+		}
+
+		sDeviceList.InsertBefore(nextDevice, this);
+
 		return B_OK;
 	}
 
@@ -268,6 +300,15 @@ struct RawDevice : Device, DoublyLinkedListLinkImpl<RawDevice> {
 		}
 	}
 
+	void GetInfo(ram_disk_ioctl_info& _info) const
+	{
+		_info.id = fID;
+		_info.size = fDeviceSize;
+		memset(&_info.path, 0, sizeof(_info.path));
+		if (fFilePath != NULL)
+			strlcpy(_info.path, fFilePath, sizeof(_info.path));
+	}
+
 	status_t Flush()
 	{
 		static const size_t kPageCountPerIteration = 1024;
@@ -726,7 +767,8 @@ private:
 	}
 
 private:
-	int32			fIndex;
+	int32			fID;
+	bool			fUnregistered;
 	off_t			fDeviceSize;
 	char*			fDeviceName;
 	char*			fFilePath;
@@ -756,100 +798,157 @@ private:
 // #pragma mark -
 
 
-static bool
-parse_command_line(char* buffer, char**& _argv, int& _argc)
+static int32
+allocate_raw_device_id()
 {
-	// Process the argument string. We split at whitespace, heeding quotes and
-	// escaped characters. The processed arguments are written to the given
-	// buffer, separated by single null chars.
-	char* start = buffer;
-	char* out = buffer;
-	bool pendingArgument = false;
-	int argc = 0;
-	while (*start != '\0') {
-		// ignore whitespace
-		if (isspace(*start)) {
-			if (pendingArgument) {
-				*out = '\0';
-				out++;
-				argc++;
-				pendingArgument = false;
-			}
-			start++;
-			continue;
+	MutexLocker deviceListLocker(sDeviceListLock);
+	for (size_t i = 0; i < sizeof(sUsedRawDeviceIDs) * 8; i++) {
+		if ((sUsedRawDeviceIDs & ((uint64)1 << i)) == 0) {
+			sUsedRawDeviceIDs |= (uint64)1 << i;
+			return (int32)i;
 		}
+	}
 
-		pendingArgument = true;
+	return -1;
+}
 
-		if (*start == '"' || *start == '\'') {
-			// quoted text -- continue until closing quote
-			char quote = *start;
-			start++;
-			while (*start != '\0' && *start != quote) {
-				if (*start == '\\' && quote == '"') {
-					start++;
-					if (*start == '\0')
-						break;
-				}
-				*out = *start;
-				start++;
-				out++;
-			}
 
-			if (*start != '\0')
-				start++;
-		} else {
-			// unquoted text
-			if (*start == '\\') {
-				// escaped char
-				start++;
-				if (start == '\0')
-					break;
-			}
+static void
+free_raw_device_id(int32 id)
+{
+	MutexLocker deviceListLocker(sDeviceListLock);
+	sUsedRawDeviceIDs &= ~((uint64)1 << id);
+}
 
-			*out = *start;
-			start++;
-			out++;
-		}
-	}
 
-	if (pendingArgument) {
-		*out = '\0';
-		argc++;
+static RawDevice*
+find_raw_device(int32 id)
+{
+	for (RawDeviceList::Iterator it = sDeviceList.GetIterator();
+			RawDevice* device = it.Next();) {
+		if (device->ID() == id)
+			return device;
 	}
 
-	// allocate argument vector
-	char** argv = new(std::nothrow) char*[argc + 1];
-	if (argv == NULL)
-		return false;
+	return NULL;
+}
+
 
-	// fill vector
-	start = buffer;
-	for (int i = 0; i < argc; i++) {
-		argv[i] = start;
-		start += strlen(start) + 1;
+static status_t
+ioctl_register(ControlDevice* controlDevice, ram_disk_ioctl_register* request)
+{
+	KPath path;
+	uint64 deviceSize = 0;
+
+	if (request->path[0] != '\0') {
+		// check if the path is null-terminated
+		if (strnlen(request->path, sizeof(request->path))
+				== sizeof(request->path)) {
+			return B_BAD_VALUE;
+		}
+
+		// get a normalized file path
+		status_t error = path.SetTo(request->path, true);
+		if (error != B_OK) {
+			dprintf("ramdisk: register: Invalid path \"%s\": %s\n",
+				request->path, strerror(error));
+			return B_BAD_VALUE;
+		}
+
+		struct stat st;
+		if (lstat(path.Path(), &st) != 0) {
+			dprintf("ramdisk: register: Failed to stat \"%s\": %s\n",
+				path.Path(), strerror(errno));
+			return errno;
+		}
+
+		if (!S_ISREG(st.st_mode)) {
+			dprintf("ramdisk: register: \"%s\" is not a file!\n", path.Path());
+			return B_BAD_VALUE;
+		}
+
+		deviceSize = st.st_size;
+	} else {
+		deviceSize = request->size;
 	}
-	argv[argc] = NULL;
 
-	_argv = argv;
-	_argc = argc;
-	return true;
+	return controlDevice->Register(path.Length() > 0 ? path.Path() : NULL,
+		deviceSize, request->id);
 }
 
 
-static RawDevice*
-find_raw_device(const char* deviceName)
+static status_t
+ioctl_unregister(ControlDevice* controlDevice,
+	ram_disk_ioctl_unregister* request)
 {
-	if (strncmp(deviceName, "/dev/", 5) == 0)
-		deviceName += 5;
+	// find the device in the list and unregister it
+	MutexLocker locker(sDeviceListLock);
+	RawDevice* device = find_raw_device(request->id);
+	if (device == NULL)
+		return B_ENTRY_NOT_FOUND;
+
+	// mark unregistered before we unlock
+	if (device->IsUnregistered())
+		return B_BUSY;
+	device->SetUnregistered(true);
+	locker.Unlock();
+
+	device_node* node = device->Node();
+	status_t error = sDeviceManager->unpublish_device(node,
+		device->DeviceName());
+	if (error != B_OK) {
+		dprintf("ramdisk: unregister: Failed to unpublish device \"%s\": %s\n",
+			device->DeviceName(), strerror(error));
+		return error;
+	}
 
-	for (RawDeviceList::Iterator it = sDeviceList.GetIterator();
-			RawDevice* device = it.Next();) {
-		if (strcmp(device->DeviceName(), deviceName) == 0)
-			return device;
+	error = sDeviceManager->unregister_node(node);
+	// Note: B_BUSY is OK. The node will removed as soon as possible.
+	if (error != B_OK && error != B_BUSY) {
+		dprintf("ramdisk: unregister: Failed to unregister node for device %"
+			B_PRId32 ": %s\n", request->id, strerror(error));
+		return error;
 	}
 
-	return NULL;
+	return B_OK;
+}
+
+
+static status_t
+ioctl_info(RawDevice* device, ram_disk_ioctl_info* request)
+{
+	device->GetInfo(*request);
+	return B_OK;
+}
+
+
+template<typename DeviceType, typename Request>
+static status_t
+handle_ioctl(DeviceType* device,
+	status_t (*handler)(DeviceType*, Request*), void* buffer)
+{
+	// copy request to the kernel heap
+	if (buffer == NULL || !IS_USER_ADDRESS(buffer))
+		return B_BAD_ADDRESS;
+
+	Request* request = new(std::nothrow) Request;
+	if (request == NULL)
+		return B_NO_MEMORY;
+	ObjectDeleter<Request> requestDeleter(request);
+
+	if (user_memcpy(request, buffer, sizeof(Request)) != B_OK)
+		return B_BAD_ADDRESS;
+
+	// handle the ioctl
+	status_t error = handler(device, request);
+	if (error != B_OK)
+		return error;
+
+	// copy the request back to userland
+	if (user_memcpy(buffer, request, sizeof(Request)) != B_OK)
+		return B_BAD_ADDRESS;
+
+	return B_OK;
 }
 
 
@@ -890,6 +989,12 @@ ram_disk_driver_init_driver(device_node* node, void** \
_driverCookie)  uint64 deviceSize;
 	if (sDeviceManager->get_attr_uint64(node, kDeviceSizeItem, &deviceSize,
 			false) == B_OK) {
+		int32 id = -1;
+		sDeviceManager->get_attr_uint32(node, kDeviceIDItem, (uint32*)&id,
+			false);
+		if (id < 0)
+			return B_ERROR;
+
 		const char* filePath = NULL;
 		sDeviceManager->get_attr_string(node, kFilePathItem, &filePath, false);
 
@@ -897,7 +1002,7 @@ ram_disk_driver_init_driver(device_node* node, void** \
_driverCookie)  if (device == NULL)
 			return B_NO_MEMORY;
 
-		status_t error = device->Init(filePath, deviceSize);
+		status_t error = device->Init(id, filePath, deviceSize);
 		if (error != B_OK) {
 			delete device;
 			return error;
@@ -920,6 +1025,8 @@ static void
 ram_disk_driver_uninit_driver(void* driverCookie)
 {
 	Device* device = (Device*)driverCookie;
+	if (RawDevice* rawDevice = dynamic_cast<RawDevice*>(device))
+		free_raw_device_id(rawDevice->ID());
 	delete device;
 }
 
@@ -976,185 +1083,15 @@ static status_t
 ram_disk_control_device_read(void* cookie, off_t position, void* buffer,
 	size_t* _length)
 {
-	*_length = 0;
-	return B_OK;
+	return B_BAD_VALUE;
 }
 
 
-
 static status_t
 ram_disk_control_device_write(void* cookie, off_t position, const void* data,
 	size_t* _length)
 {
-	ControlDevice* device = (ControlDevice*)cookie;
-
-	if (position != 0)
-		return B_BAD_VALUE;
-
-	// copy data to stack buffer
-	char* buffer = (char*)malloc(*_length + 1);
-	if (buffer == NULL)
-		return B_NO_MEMORY;
-	MemoryDeleter bufferDeleter(buffer);
-
-	if (IS_USER_ADDRESS(data)) {
-		if (user_memcpy(buffer, data, *_length) != B_OK)
-			return B_BAD_ADDRESS;
-	} else
-		memcpy(buffer, data, *_length);
-
-	buffer[*_length] = '\0';
-
-	// parse arguments
-	char** argv;
-	int argc;
-	if (!parse_command_line(buffer, argv, argc))
-		return B_NO_MEMORY;
-	ArrayDeleter<char*> argvDeleter(argv);
-
-	if (argc == 0) {
-		dprintf("\"help\" for usage!\n");
-		return B_BAD_VALUE;
-	}
-
-	// execute command
-	if (strcmp(argv[0], "help") == 0) {
-		// help
-		dprintf("register (<path> | -s <size>)\n");
-		dprintf("  Registers a new ram disk device backed by file <path> or\n"
-			"  an unbacked ram disk device with size <size>.\n");
-		dprintf("unregister <device>\n");
-		dprintf("  Unregisters <device>.\n");
-		dprintf("flush <device>\n");
-		dprintf("  Writes <device> changes back to the file associated with\n"
-			"  it, if any.\n");
-	} else if (strcmp(argv[0], "register") == 0) {
-		// register
-		if (argc < 2 || argc > 3 || (argc == 3 && strcmp(argv[1], "-s") != 0)) {
-			dprintf("Usage: register (<path> | -s <size>)\n");
-			return B_BAD_VALUE;
-		}
-
-		KPath path;
-		uint64 deviceSize = 0;
-
-		if (argc == 2) {
-			// get a normalized file path
-			status_t error = path.SetTo(argv[1], true);
-			if (error != B_OK) {
-				dprintf("Invalid path \"%s\": %s\n", argv[1], strerror(error));
-				return B_BAD_VALUE;
-			}
-
-			struct stat st;
-			if (lstat(path.Path(), &st) != 0) {
-				dprintf("Failed to stat \"%s\": %s\n", path.Path(),
-					strerror(errno));
-				return errno;
-			}
-
-			if (!S_ISREG(st.st_mode)) {
-				dprintf("\"%s\" is not a file!\n", path.Path());
-				return B_BAD_VALUE;
-			}
-
-			deviceSize = st.st_size;
-		} else {
-			// parse size argument
-			const char* sizeString = argv[2];
-			char* end;
-			deviceSize = strtoll(sizeString, &end, 0);
-			if (end == sizeString) {
-				dprintf("Invalid size argument: \"%s\"\n", sizeString);
-				return B_BAD_VALUE;
-			}
-
-			switch (*end) {
-				case 'g':
-					deviceSize *= 1024;
-				case 'm':
-					deviceSize *= 1024;
-				case 'k':
-					deviceSize *= 1024;
-					break;
-			}
-		}
-
-		return device->Register(path.Length() > 0 ? path.Path() : NULL,
-			deviceSize);
-	} else if (strcmp(argv[0], "unregister") == 0) {
-		// unregister
-		if (argc != 2) {
-			dprintf("Usage: unregister <device>\n");
-			return B_BAD_VALUE;
-		}
-
-		const char* deviceName = argv[1];
-
-		// find the device in the list and unregister it
-		MutexLocker locker(sDeviceListLock);
-		if (RawDevice* device = find_raw_device(deviceName)) {
-			// TODO: Race condition: We should mark the device as going to
-			// be unregistered, so no one else can try the same after we
-			// unlock!
-			locker.Unlock();
-// TODO: The following doesn't work! unpublish_device(), as per implementation
-// (partially commented out) and unregister_node() returns B_BUSY.
-			status_t error = sDeviceManager->unpublish_device(
-				device->Node(), device->DeviceName());
-			if (error != B_OK) {
-				dprintf("Failed to unpublish device \"%s\": %s\n",
-					deviceName, strerror(error));
-				return error;
-			}
-
-			error = sDeviceManager->unregister_node(device->Node());
-			if (error != B_OK) {
-				dprintf("Failed to unregister node \"%s\": %s\n",
-					deviceName, strerror(error));
-				return error;
-			}
-
-			return B_OK;
-		}
-
-		dprintf("Device \"%s\" not found!\n", deviceName);
-		return B_BAD_VALUE;
-	} else if (strcmp(argv[0], "flush") == 0) {
-		// flush
-		if (argc != 2) {
-			dprintf("Usage: unregister <device>\n");
-			return B_BAD_VALUE;
-		}
-
-		const char* deviceName = argv[1];
-
-		// find the device in the list and flush it
-		MutexLocker locker(sDeviceListLock);
-		RawDevice* device = find_raw_device(deviceName);
-		if (device == NULL) {
-			dprintf("Device \"%s\" not found!\n", deviceName);
-			return B_BAD_VALUE;
-		}
-
-		// TODO: Race condition: Once we unlock someone could unregister the
-		// device. We should probably open the device by path, and use a
-		// special ioctl.
-		locker.Unlock();
-
-		status_t error = device->Flush();
-		if (error != B_OK) {
-			dprintf("Failed to flush device: %s\n", strerror(error));
-			return error;
-		}
-
-		return B_OK;
-	} else {
-		dprintf("Invalid command \"%s\"!\n", argv[0]);
-		return B_BAD_VALUE;
-	}
-
-	return B_OK;
+	return B_BAD_VALUE;
 }
 
 
@@ -1162,6 +1099,16 @@ static status_t
 ram_disk_control_device_control(void* cookie, uint32 op, void* buffer,
 	size_t length)
 {
+	ControlDevice* device = (ControlDevice*)cookie;
+
+	switch (op) {
+		case RAM_DISK_IOCTL_REGISTER:
+			return handle_ioctl(device, &ioctl_register, buffer);
+
+		case RAM_DISK_IOCTL_UNREGISTER:
+			return handle_ioctl(device, &ioctl_unregister, buffer);
+	}
+
 	return B_BAD_VALUE;
 }
 
@@ -1347,6 +1294,21 @@ ram_disk_raw_device_control(void* _cookie, uint32 op, void* \
buffer,  case B_SET_INTERRUPTABLE_IO:
 		case B_FLUSH_DRIVE_CACHE:
 			return B_OK;
+
+		case RAM_DISK_IOCTL_FLUSH:
+		{
+			status_t error = device->Flush();
+			if (error != B_OK) {
+				dprintf("ramdisk: flush: Failed to flush device: %s\n",
+					strerror(error));
+				return error;
+			}
+
+			return B_OK;
+		}
+
+		case RAM_DISK_IOCTL_INFO:
+			return handle_ioctl(device, &ioctl_info, buffer);
 	}
 
 	return B_BAD_VALUE;
diff --git a/src/bin/Jamfile b/src/bin/Jamfile
index 5cb2794..67c51e6 100644
--- a/src/bin/Jamfile
+++ b/src/bin/Jamfile
@@ -126,6 +126,11 @@ StdBinCommands
 	urlwrapper.cpp
 	: be $(TARGET_LIBSUPC++) : $(haiku-utils_rsrc) ;
 
+# standard commands that need libbe.so, libsupc++.so, and libshared.a
+StdBinCommands
+	ramdisk.cpp
+	: libshared.a be $(TARGET_LIBSUPC++) : $(haiku-utils_rsrc) ;
+
 # commands that need libbe.so and the stub catalog
 StdBinCommands
 	clockconfig.cpp
diff --git a/src/bin/ramdisk.cpp b/src/bin/ramdisk.cpp
new file mode 100644
index 0000000..b6070ea
--- /dev/null
+++ b/src/bin/ramdisk.cpp
@@ -0,0 +1,477 @@
+/*
+ * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
+ * Distributed under the terms of the MIT License.
+ */
+
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <Entry.h>
+#include <Path.h>
+#include <String.h>
+
+#include <AutoDeleter.h>
+#include <TextTable.h>
+
+#include <file_systems/ram_disk/ram_disk.h>
+
+
+extern const char* __progname;
+static const char* kProgramName = __progname;
+
+static const char* const kUsage =
+	"Usage: %s <command> [ <options> ]\n"
+	"Controls RAM disk devices.\n"
+	"\n"
+	"Commands:\n"
+	"  create (-s <size> | <path>)\n"
+	"    Creates a new RAM disk.\n"
+	"  delete <id>\n"
+	"    Deletes an existing RAM disk.\n"
+	"  flush <id>\n"
+	"    Writes modified data of an existing RAM disk back to its file.\n"
+	"  help\n"
+	"    Print this usage info.\n"
+	"  list\n"
+	"    List all RAM disks.\n"
+;
+
+static const char* const kCreateUsage =
+	"Usage: %s %s (-s <size> | <path>)\n"
+	"Creates a new RAM disk device. If the <size> argument is specified, a\n"
+	"new zeroed RAM disk with that size (in bytes, suffixes 'k', 'm', 'g' are\n"
+	"interpreted as KiB, MiB, GiB) is registered.\n"
+	"Alternatively a file path can be specified. In that case the RAM disk \n"
+	"data are initially read from that file and at any later point the\n"
+	"modified RAM disk data can be written back to the same file upon request\n"
+	"(via the \"flush\" command). The size of the RAM disk is implied by that\n"
+	"of the file.\n"
+;
+
+static const char* const kDeleteUsage =
+	"Usage: %s %s <id>\n"
+	"Deletes the existing RAM disk with ID <id>. All modified data will be\n"
+	"lost.\n"
+;
+
+static const char* const kFlushUsage =
+	"Usage: %s %s <id>\n"
+	"Writes all modified data of the RAM disk with ID <id> back to the file\n"
+	"specified when the RAM disk was created. Fails, if the RAM disk had been\n"
+	"created without an associated file.\n"
+;
+
+static const char* const kListUsage =
+	"Usage: %s %s\n"
+	"Lists all existing RAM disks.\n"
+;
+
+static const char* const kRamDiskControlDevicePath
+	= "/dev/" RAM_DISK_CONTROL_DEVICE_NAME;
+static const char* const kRamDiskRawDeviceBasePath
+	= "/dev/" RAM_DISK_RAW_DEVICE_BASE_NAME;
+
+static const char* sCommandName = NULL;
+static const char* sCommandUsage = NULL;
+
+
+static void
+print_usage_and_exit(bool error)
+{
+	if (sCommandUsage != NULL) {
+	    fprintf(error ? stderr : stdout, sCommandUsage, kProgramName,
+			sCommandName);
+	} else
+	    fprintf(error ? stderr : stdout, kUsage, kProgramName);
+    exit(error ? 1 : 0);
+}
+
+
+static status_t
+execute_control_device_ioctl(int operation, void* request)
+{
+	// open the ram disk control device
+	int fd = open(kRamDiskControlDevicePath, O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "Error: Failed to open RAM disk control device \"%s\": "
+			"%s\n", kRamDiskControlDevicePath, strerror(errno));
+		return errno;
+	}
+	FileDescriptorCloser fdCloser(fd);
+
+	// issue the request
+	if (ioctl(fd, operation, request) < 0)
+		return errno;
+
+	return B_OK;
+}
+
+
+static int
+command_register(int argc, const char* const* argv)
+{
+	sCommandUsage = kCreateUsage;
+
+	int64 deviceSize = -1;
+
+	while (true) {
+		static struct option sLongOptions[] = {
+			{ "size", required_argument, 0, 's' },
+			{ "help", no_argument, 0, 'h' },
+			{ 0, 0, 0, 0 }
+		};
+
+		opterr = 0; // don't print errors
+		int c = getopt_long(argc, (char**)argv, "+s:h", sLongOptions, NULL);
+		if (c == -1)
+			break;
+
+		switch (c) {
+			case 'h':
+				print_usage_and_exit(false);
+				break;
+
+			case 's':
+			{
+				const char* sizeString = optarg;
+				char* end;
+				deviceSize = strtoll(sizeString, &end, 0);
+				if (end != sizeString && deviceSize > 0) {
+					int64 originalDeviceSize = deviceSize;
+					switch (*end) {
+						case 'g':
+							deviceSize *= 1024;
+						case 'm':
+							deviceSize *= 1024;
+						case 'k':
+							deviceSize *= 1024;
+							end++;
+							break;
+						case '\0':
+							break;
+						default:
+							deviceSize = -1;
+							break;
+					}
+
+					if (deviceSize > 0 && originalDeviceSize > deviceSize)
+						deviceSize = -1;
+				}
+
+				if (deviceSize <= 0) {
+					fprintf(stderr, "Error: Invalid size argument: \"%s\"\n",
+						sizeString);
+					return 1;
+				}
+
+				// check maximum size
+				system_info info;
+				get_system_info(&info);
+				if (deviceSize / B_PAGE_SIZE > (int64)info.max_pages * 2 / 3) {
+					fprintf(stderr, "Error: Given RAM disk size too large.\n");
+					return 1;
+				}
+
+				break;
+			}
+
+			default:
+				print_usage_and_exit(true);
+				break;
+		}
+	}
+
+	// The remaining optional argument is the file path. It may only be
+	// specified, if no size has been specified.
+	const char* path = optind < argc ? argv[optind++] : NULL;
+	if (optind < argc || (deviceSize >= 0) == (path != NULL))
+		print_usage_and_exit(true);
+
+	// prepare the request
+	ram_disk_ioctl_register request;
+	request.size = (uint64)deviceSize;
+	request.path[0] = '\0';
+	request.id = -1;
+
+	if (path != NULL) {
+		// verify the path
+		BEntry entry;
+		status_t error = entry.SetTo(path, true);
+		if (error == B_OK && !entry.Exists())
+			error = B_ENTRY_NOT_FOUND;
+		if (error != B_OK) {
+			fprintf(stderr, "Error: Failed to resolve path \"%s\": %s\n",
+				path, strerror(error));
+			return 1;
+		}
+
+		if (!entry.IsFile()) {
+			fprintf(stderr, "Error: \"%s\" is not a file.\n", path);
+			return 1;
+		}
+
+		BPath normalizedPath;
+		error = entry.GetPath(&normalizedPath);
+		if (error != B_OK) {
+			fprintf(stderr, "Error: Failed to normalize path \"%s\": %s\n",
+				path, strerror(error));
+			return 1;
+		}
+
+		if (strlcpy(request.path, normalizedPath.Path(), sizeof(request.path))
+				>= sizeof(request.path)) {
+			fprintf(stderr, "Error: Normalized path too long.\n");
+			return 1;
+		}
+	}
+
+	status_t error = execute_control_device_ioctl(RAM_DISK_IOCTL_REGISTER,
+		&request);
+	if (error != B_OK) {
+		fprintf(stderr, "Error: Failed to create RAM disk device: %s\n",
+			strerror(error));
+		return 1;
+	}
+
+	printf("RAM disk device created as \"%s/%" B_PRId32 "/raw\"\n",
+		kRamDiskRawDeviceBasePath, request.id);
+	return 0;
+}
+
+
+static int
+command_unregister(int argc, const char* const* argv)
+{
+	sCommandUsage = kDeleteUsage;
+
+	while (true) {
+		static struct option sLongOptions[] = {
+			{ "help", no_argument, 0, 'h' },
+			{ 0, 0, 0, 0 }
+		};
+
+		opterr = 0; // don't print errors
+		int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL);
+		if (c == -1)
+			break;
+
+		switch (c) {
+			case 'h':
+				print_usage_and_exit(false);
+				break;
+
+			default:
+				print_usage_and_exit(true);
+				break;
+		}
+	}
+
+	// The remaining argument is the device ID.
+	if (optind + 1 != argc)
+		print_usage_and_exit(true);
+
+	const char* idString = argv[optind++];
+	char* end;
+	long long id = strtol(idString, &end, 0);
+	if (end == idString || *end != '\0' || id < 0 || id > INT32_MAX) {
+		fprintf(stderr, "Error: Invalid ID \"%s\".\n", idString);
+		return 1;
+	}
+
+	// check whether the raw device for that ID exists
+	BString path;
+	path.SetToFormat("%s/%s/raw", kRamDiskRawDeviceBasePath, idString);
+	struct stat st;
+	if (lstat(path, &st) != 0) {
+		fprintf(stderr, "Error: No RAM disk with ID %s.\n", idString);
+		return 1;
+	}
+
+	// issue the request
+	ram_disk_ioctl_unregister request;
+	request.id = (int32)id;
+
+	status_t error = execute_control_device_ioctl(RAM_DISK_IOCTL_UNREGISTER,
+		&request);
+	if (error != B_OK) {
+		fprintf(stderr, "Error: Failed to delete RAM disk device: %s\n",
+			strerror(error));
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static int
+command_flush(int argc, const char* const* argv)
+{
+	sCommandUsage = kFlushUsage;
+
+	while (true) {
+		static struct option sLongOptions[] = {
+			{ "help", no_argument, 0, 'h' },
+			{ 0, 0, 0, 0 }
+		};
+
+		opterr = 0; // don't print errors
+		int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL);
+		if (c == -1)
+			break;
+
+		switch (c) {
+			case 'h':
+				print_usage_and_exit(false);
+				break;
+
+			default:
+				print_usage_and_exit(true);
+				break;
+		}
+	}
+
+	// The remaining argument is the device ID.
+	if (optind + 1 != argc)
+		print_usage_and_exit(true);
+
+	const char* idString = argv[optind++];
+	char* end;
+	long long id = strtol(idString, &end, 0);
+	if (end == idString || *end != '\0' || id < 0 || id > INT32_MAX) {
+		fprintf(stderr, "Error: Invalid ID \"%s\".\n", idString);
+		return 1;
+	}
+
+	// open the raw device
+	BString path;
+	path.SetToFormat("%s/%s/raw", kRamDiskRawDeviceBasePath, idString);
+	int fd = open(path, O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "Error: Failed to open RAM disk device \"%s\"\n",
+			path.String());
+		return 1;
+	}
+	FileDescriptorCloser fdCloser(fd);
+
+	// issue the request
+	if (ioctl(fd, RAM_DISK_IOCTL_FLUSH, NULL) < 0) {
+		fprintf(stderr, "Error: Failed to flush RAM disk device: %s\n",
+			strerror(errno));
+		return 1;
+	}
+
+	return 0;
+}
+
+
+static int
+command_list(int argc, const char* const* argv)
+{
+	sCommandUsage = kListUsage;
+
+	while (true) {
+		static struct option sLongOptions[] = {
+			{ "help", no_argument, 0, 'h' },
+			{ 0, 0, 0, 0 }
+		};
+
+		opterr = 0; // don't print errors
+		int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL);
+		if (c == -1)
+			break;
+
+		switch (c) {
+			case 'h':
+				print_usage_and_exit(false);
+				break;
+
+			default:
+				print_usage_and_exit(true);
+				break;
+		}
+	}
+
+	// There shouldn't be any remaining arguments.
+	if (optind != argc)
+		print_usage_and_exit(true);
+
+	// iterate through the RAM disk device directory and search for raw devices
+	DIR* dir = opendir(kRamDiskRawDeviceBasePath);
+	if (dir == NULL) {
+		fprintf(stderr, "Error: Failed to open RAM disk device directory: %s\n",
+			strerror(errno));
+		return 1;
+	}
+	CObjectDeleter<DIR, int> dirCloser(dir, &closedir);
+
+	TextTable table;
+	table.AddColumn("ID", B_ALIGN_RIGHT);
+	table.AddColumn("Size", B_ALIGN_RIGHT);
+	table.AddColumn("Associated file");
+
+	while (dirent* entry = readdir(dir)) {
+		// check, if the entry name could be an ID
+		const char* idString = entry->d_name;
+		char* end;
+		long long id = strtol(idString, &end, 0);
+		if (end == idString || *end != '\0' || id < 0 || id > INT32_MAX)
+			continue;
+
+		// open the raw device
+		BString path;
+		path.SetToFormat("%s/%s/raw", kRamDiskRawDeviceBasePath, idString);
+		int fd = open(path, O_RDONLY);
+		if (fd < 0)
+			continue;
+		FileDescriptorCloser fdCloser(fd);
+
+		// issue the request
+		ram_disk_ioctl_info request;
+		if (ioctl(fd, RAM_DISK_IOCTL_INFO, &request) < 0)
+			continue;
+
+		int32 rowIndex = table.CountRows();
+		table.SetTextAt(rowIndex, 0, BString() << request.id);
+		table.SetTextAt(rowIndex, 1, BString() << request.size);
+		table.SetTextAt(rowIndex, 2, request.path);
+	}
+
+	if (table.CountRows() > 0)
+		table.Print(INT32_MAX);
+	else
+		printf("No RAM disks.\n");
+
+	return 0;
+}
+
+
+int
+main(int argc, const char* const* argv)
+{
+	if (argc < 2)
+		print_usage_and_exit(true);
+
+	if (strcmp(argv[1], "help") == 0 || strcmp(argv[1], "--help") == 0
+		|| strcmp(argv[1], "-h") == 0) {
+		print_usage_and_exit(false);
+	}
+
+	sCommandName = argv[1];
+
+	if (strcmp(sCommandName, "create") == 0)
+		return command_register(argc - 1, argv + 1);
+	if (strcmp(sCommandName, "delete") == 0)
+		return command_unregister(argc - 1, argv + 1);
+	if (strcmp(sCommandName, "flush") == 0)
+		return command_flush(argc - 1, argv + 1);
+	if (strcmp(sCommandName, "list") == 0)
+		return command_list(argc - 1, argv + 1);
+
+	print_usage_and_exit(true);
+}


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic