text: /*
   * Copyright 2010, Haiku Inc. All rights reserved.
   * Distributed under the terms of the MIT License.
   *
   * Authors:
   *		Michael Lotz <mmlr@mlotz.ch>
   */
  
  #include <new>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  
  #include <errno.h>
  #include <dirent.h>
  
  #include <fs_info.h>
  #include <fs_interface.h>
  
  #include <debug.h>
  #include <KernelExport.h>
  
  #include <io_requests.h>
  
  static const char *kLogFilePrefix = "/var/log/log_overlay";
  
  
  #define DO_LOG(format, args...) \
  	{ \
  		char _printBuffer[256]; \
  		int _printSize = snprintf(_printBuffer, sizeof(_printBuffer), \
  			"%" B_PRId64 " %" B_PRId32 " %p: " format, system_time(), \
  			find_thread(NULL), \
  			((fs_vnode *)vnode->private_node)->private_node, args); \
  		if ((unsigned int)_printSize > sizeof(_printBuffer)) { \
  			_printBuffer[sizeof(_printBuffer) - 1] = '\n'; \
  			_printSize = sizeof(_printBuffer); \
  		} \
  		write((int)(addr_t)volume->private_volume, _printBuffer, _printSize); \
  	}
  
  #define OVERLAY_CALL(op, params...) \
  	status_t result = B_UNSUPPORTED; \
  	fs_vnode *superVnode = (fs_vnode *)vnode->private_node; \
  	if (superVnode->ops->op != NULL) \
  		result = superVnode->ops->op(volume->super_volume, superVnode, params);
  
  
  static status_t
  overlay_put_vnode(fs_volume *volume, fs_vnode *vnode, bool reenter)
  {
  	DO_LOG("put_vnode reenter: %s\n", reenter ? "yes" : "no");
  
  	status_t result = B_UNSUPPORTED;
  	fs_vnode *superVnode = (fs_vnode *)vnode->private_node;
  	if (superVnode->ops->put_vnode != NULL) {
  		result = superVnode->ops->put_vnode(volume->super_volume, superVnode,
  			reenter);
  	}
  
  	DO_LOG("put_vnode result: %#" B_PRIx32 "\n", result);
  	delete (fs_vnode *)vnode->private_node;
  	return result;
  }
  
  
  static status_t
  overlay_remove_vnode(fs_volume *volume, fs_vnode *vnode, bool reenter)
  {
  	DO_LOG("remove_vnode reenter: %s\n", reenter ? "yes" : "no");
  
  	status_t result = B_UNSUPPORTED;
  	fs_vnode *superVnode = (fs_vnode *)vnode->private_node;
  	if (superVnode->ops->remove_vnode != NULL) {
  		result = superVnode->ops->remove_vnode(volume->super_volume, superVnode,
  			reenter);
  	}
  
  	DO_LOG("remove_vnode result: %#" B_PRIx32 "\n", result);
  	delete (fs_vnode *)vnode->private_node;
  	return result;
  }
  
  
  static status_t
  overlay_get_super_vnode(fs_volume *volume, fs_vnode *vnode,
  	fs_volume *superVolume, fs_vnode *_superVnode)
  {
  	if (volume == superVolume) {
  		*_superVnode = *vnode;
  		return B_OK;
  	}
  
  	fs_vnode *superVnode = (fs_vnode *)vnode->private_node;
  	if (superVnode->ops->get_super_vnode != NULL) {
  		return superVnode->ops->get_super_vnode(volume->super_volume,
  			superVnode, superVolume, _superVnode);
  	}
  
  	*_superVnode = *superVnode;
  	return B_OK;
  }
  
  
  static status_t
  overlay_lookup(fs_volume *volume, fs_vnode *vnode, const char *name, ino_t *id)
  {
  	DO_LOG("lookup name: \"%s\"\n", name);
  	OVERLAY_CALL(lookup, name, id)
  	DO_LOG("lookup result: %#" B_PRIx32 "; id: %" B_PRIdINO "\n", result, *id);
  	return result;
  }
  
  
  static status_t
  overlay_get_vnode_name(fs_volume *volume, fs_vnode *vnode, char *buffer,
  	size_t bufferSize)
  {
  	DO_LOG("get_vnode_name buffer: %p; buffer_size: %" B_PRIuSIZE "\n", buffer,
  		bufferSize);
  	OVERLAY_CALL(get_vnode_name, buffer, bufferSize)
  	DO_LOG("get_vnode_name result: %#" B_PRIx32 "; buffer: \"%s\"\n", result,
  		result == B_OK ? buffer : "unsafe");
  	return result;
  }
  
  
  static bool
  overlay_can_page(fs_volume *volume, fs_vnode *vnode, void *cookie)
  {
  	DO_LOG("can_page cookie: %p\n", cookie);
  	bool result = false;
  	fs_vnode *superVnode = (fs_vnode *)vnode->private_node;
  	if (superVnode->ops->can_page != NULL) {
  		result = superVnode->ops->can_page(volume->super_volume, superVnode,
  			cookie);
  	}
  
  	DO_LOG("can_page result: %s\n", result ? "yes" : "no");
  	return result;
  }
  
  
  static status_t
  overlay_read_pages(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos,
  	const iovec *vecs, size_t count, size_t *numBytes)
  {
  	DO_LOG("read_pages cookie: %p; pos: %" B_PRIdOFF "; vecs: %p; count: %"
  		B_PRIuSIZE "; num_bytes: %" B_PRIuSIZE "\n", cookie, pos, vecs, count,
  		*numBytes);
  	OVERLAY_CALL(read_pages, cookie, pos, vecs, count, numBytes)
  	DO_LOG("read_pages result: %#" B_PRIx32 "; num_bytes: %" B_PRIuSIZE "\n",
  		result, *numBytes);
  	return result;
  }
  
  
  static status_t
  overlay_write_pages(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos,
  	const iovec *vecs, size_t count, size_t *numBytes)
  {
  	DO_LOG("write_pages cookie: %p; pos: %" B_PRIdOFF "; vecs: %p; count: %"
  		B_PRIuSIZE "; num_bytes: %" B_PRIuSIZE "\n", cookie, pos, vecs, count,
  		*numBytes);
  	OVERLAY_CALL(write_pages, cookie, pos, vecs, count, numBytes)
  	DO_LOG("write_pages result: %#" B_PRIx32 "; num_bytes: %" B_PRIuSIZE "\n",
  		result, *numBytes);
  	return result;
  }
  
  
  static status_t
  overlay_io(fs_volume *volume, fs_vnode *vnode, void *cookie,
  	io_request *request)
  {
  	DO_LOG("io cookie: %p; request: %p (write: %s; offset: %" B_PRIdOFF
  		"; length: %" B_PRIdOFF ")\n", cookie, request,
  		io_request_is_write(request) ? "yes" : "no", io_request_offset(request),
  		io_request_length(request));
  	OVERLAY_CALL(io, cookie, request)
  	DO_LOG("io result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_cancel_io(fs_volume *volume, fs_vnode *vnode, void *cookie,
  	io_request *request)
  {
  	DO_LOG("cancel_io cookie: %p; request: %p\n", cookie, request);
  	OVERLAY_CALL(cancel_io, cookie, request)
  	DO_LOG("cancel_io result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_get_file_map(fs_volume *volume, fs_vnode *vnode, off_t offset,
  	size_t size, struct file_io_vec *vecs, size_t *count)
  {
  	DO_LOG("get_file_map offset: %" B_PRIdOFF "; size: %" B_PRIuSIZE
  		"; vecs: %p; count: %" B_PRIuSIZE "\n", offset, size, vecs, *count);
  	OVERLAY_CALL(get_file_map, offset, size, vecs, count)
  	DO_LOG("get_file_map result: %#" B_PRIx32 "; count: %" B_PRIuSIZE "\n",
  		result, *count);
  	return result;
  }
  
  
  static status_t
  overlay_ioctl(fs_volume *volume, fs_vnode *vnode, void *cookie, uint32 op,
  	void *buffer, size_t length)
  {
  	DO_LOG("ioctl cookie: %p; op: %" B_PRIu32 "; buffer: %p; size: %" B_PRIuSIZE
  		"\n", cookie, op, buffer, length);
  	OVERLAY_CALL(ioctl, cookie, op, buffer, length)
  	DO_LOG("ioctl result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_set_flags(fs_volume *volume, fs_vnode *vnode, void *cookie,
  	int flags)
  {
  	DO_LOG("set_flags cookie: %p; flags: %#x\n", cookie, flags);
  	OVERLAY_CALL(set_flags, cookie, flags)
  	DO_LOG("set_flags result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_select(fs_volume *volume, fs_vnode *vnode, void *cookie, uint8 event,
  	selectsync *sync)
  {
  	DO_LOG("select cookie: %p; event: %" B_PRIu8 "; selectsync: %p\n", cookie,
  		event, sync);
  	OVERLAY_CALL(select, cookie, event, sync)
  	DO_LOG("select result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_deselect(fs_volume *volume, fs_vnode *vnode, void *cookie, uint8 event,
  	selectsync *sync)
  {
  	DO_LOG("deselect cookie: %p; event: %" B_PRIu8 "; selectsync: %p\n", cookie,
  		event, sync);
  	OVERLAY_CALL(deselect, cookie, event, sync)
  	DO_LOG("deselect result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_fsync(fs_volume *volume, fs_vnode *vnode)
  {
  	DO_LOG("%s\n", "fsync");
  
  	status_t result = B_UNSUPPORTED;
  	fs_vnode *superVnode = (fs_vnode *)vnode->private_node;
  	if (superVnode->ops->fsync != NULL)
  		result = superVnode->ops->fsync(volume->super_volume, superVnode);
  
  	DO_LOG("fsync result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_read_symlink(fs_volume *volume, fs_vnode *vnode, char *buffer,
  	size_t *bufferSize)
  {
  	DO_LOG("read_symlink buffer: %p; buffer_size: %" B_PRIuSIZE "\n", buffer,
  		*bufferSize);
  	OVERLAY_CALL(read_symlink, buffer, bufferSize)
  	DO_LOG("read_symlink result: %#" B_PRIx32 "; buffer_size: %" B_PRIuSIZE
  		"; \"%.*s\"\n", result, *bufferSize,
  		result == B_OK ? (int)*bufferSize : 6,
  		result == B_OK ? buffer : "unsafe");
  	return result;
  }
  
  
  static status_t
  overlay_create_symlink(fs_volume *volume, fs_vnode *vnode, const char *name,
  	const char *path, int mode)
  {
  	DO_LOG("create_symlink name: \"%s\"; path: \"%s\"; mode: %#x\n", name, path,
  		mode);
  	OVERLAY_CALL(create_symlink, name, path, mode)
  	DO_LOG("create_symlink result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_link(fs_volume *volume, fs_vnode *vnode, const char *name,
  	fs_vnode *target)
  {
  	DO_LOG("link name: \"%s\"; target: %p\n", name,
  		((fs_vnode *)target->private_node)->private_node);
  	OVERLAY_CALL(link, name, (fs_vnode *)target->private_node)
  	DO_LOG("link result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_unlink(fs_volume *volume, fs_vnode *vnode, const char *name)
  {
  	DO_LOG("unlink name: \"%s\"\n", name);
  	OVERLAY_CALL(unlink, name)
  	DO_LOG("unlink result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_rename(fs_volume *volume, fs_vnode *vnode,
  	const char *fromName, fs_vnode *toDir, const char *toName)
  {
  	DO_LOG("rename from_name: \"%s\"; to_dir: %p; to_name: \"%s\"\n",
  		fromName, ((fs_vnode *)toDir->private_node)->private_node, toName);
  	OVERLAY_CALL(rename, fromName, (fs_vnode *)toDir->private_node, toName)
  	DO_LOG("rename result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_access(fs_volume *volume, fs_vnode *vnode, int mode)
  {
  	DO_LOG("access mode: %#x\n", mode);
  	OVERLAY_CALL(access, mode)
  	DO_LOG("access result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_read_stat(fs_volume *volume, fs_vnode *vnode, struct stat *stat)
  {
  	DO_LOG("read_stat stat: %p\n", stat);
  	OVERLAY_CALL(read_stat, stat)
  	if (result == B_OK) {
  		DO_LOG("read_stat result: %#" B_PRIx32 "; stat(dev: %" B_PRIdDEV
  			"; ino: %" B_PRIdINO "; mode: %#x; uid: %u; gid %u; size: %"
  			B_PRIdOFF ")\n", result, stat->st_dev, stat->st_ino, stat->st_mode,
  			stat->st_uid, stat->st_gid, stat->st_size);
  	} else
  		DO_LOG("read_stat result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_write_stat(fs_volume *volume, fs_vnode *vnode, const struct stat *stat,
  	uint32 statMask)
  {
  	DO_LOG("write_stat stat: %p; mask: %" B_PRIu32 "\n", stat, statMask);
  	OVERLAY_CALL(write_stat, stat, statMask)
  	DO_LOG("write_stat result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_create(fs_volume *volume, fs_vnode *vnode, const char *name,
  	int openMode, int perms, void **cookie, ino_t *newVnodeID)
  {
  	DO_LOG("create name: \"%s\"; open_mode: %#x; perms: %#x\n", name, openMode,
  		perms);
  	OVERLAY_CALL(create, name, openMode, perms, cookie, newVnodeID)
  	DO_LOG("create result: %#" B_PRIx32 "; cookie: %p; new_vnode_id: %"
  		B_PRIdINO "\n", result, *cookie, *newVnodeID);
  	return result;
  }
  
  
  static status_t
  overlay_open(fs_volume *volume, fs_vnode *vnode, int openMode, void **cookie)
  {
  	DO_LOG("open open_mode: %#x\n", openMode);
  	OVERLAY_CALL(open, openMode, cookie)
  	DO_LOG("open result: %#" B_PRIx32 "; cookie: %p\n", result, *cookie);
  	return result;
  }
  
  
  static status_t
  overlay_close(fs_volume *volume, fs_vnode *vnode, void *cookie)
  {
  	DO_LOG("close cookie %p\n", cookie);
  	OVERLAY_CALL(close, cookie)
  	DO_LOG("close result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_free_cookie(fs_volume *volume, fs_vnode *vnode, void *cookie)
  {
  	DO_LOG("free_cookie cookie %p\n", cookie);
  	OVERLAY_CALL(free_cookie, cookie)
  	DO_LOG("free_cookie result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_read(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos,
  	void *buffer, size_t *length)
  {
  	DO_LOG("read cookie: %p; pos: %" B_PRIdOFF "; buffer: %p; length: %"
  		B_PRIuSIZE "\n", cookie, pos, buffer, *length);
  	OVERLAY_CALL(read, cookie, pos, buffer, length)
  	DO_LOG("read result: %#" B_PRIx32 "; length: %" B_PRIuSIZE "\n", result,
  		*length);
  	return result;
  }
  
  
  static status_t
  overlay_write(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos,
  	const void *buffer, size_t *length)
  {
  	DO_LOG("write cookie: %p; pos: %" B_PRIdOFF "; buffer: %p; length: %"
  		B_PRIuSIZE "\n", cookie, pos, buffer, *length);
  	OVERLAY_CALL(write, cookie, pos, buffer, length)
  	DO_LOG("write result: %#" B_PRIx32 "; length: %" B_PRIuSIZE "\n", result,
  		*length);
  	return result;
  }
  
  
  static status_t
  overlay_create_dir(fs_volume *volume, fs_vnode *vnode, const char *name,
  	int perms)
  {
  	DO_LOG("create_dir name: \"%s\"; perms: %#x\n", name, perms);
  	OVERLAY_CALL(create_dir, name, perms)
  	DO_LOG("create_dir result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_remove_dir(fs_volume *volume, fs_vnode *vnode, const char *name)
  {
  	DO_LOG("remove_dir name: \"%s\"\n", name);
  	OVERLAY_CALL(remove_dir, name)
  	DO_LOG("remove_dir result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_open_dir(fs_volume *volume, fs_vnode *vnode, void **cookie)
  {
  	DO_LOG("%s\n", "open_dir");
  	OVERLAY_CALL(open_dir, cookie)
  	DO_LOG("open_dir result: %#" B_PRIx32 "; cookie: %p\n", result, *cookie);
  	return result;
  }
  
  
  static status_t
  overlay_close_dir(fs_volume *volume, fs_vnode *vnode, void *cookie)
  {
  	DO_LOG("close_dir cookie: %p\n", cookie);
  	OVERLAY_CALL(close_dir, cookie)
  	DO_LOG("close_dir result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_free_dir_cookie(fs_volume *volume, fs_vnode *vnode, void *cookie)
  {
  	DO_LOG("free_dir_cookie cookie: %p\n", cookie);
  	OVERLAY_CALL(free_dir_cookie, cookie)
  	DO_LOG("free_dir_cookie result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_read_dir(fs_volume *volume, fs_vnode *vnode, void *cookie,
  	struct dirent *buffer, size_t bufferSize, uint32 *num)
  {
  	DO_LOG("read_dir cookie: %p; buffer: %p; buffer_size: %" B_PRIuSIZE "\n",
  		cookie, buffer, bufferSize);
  	OVERLAY_CALL(read_dir, cookie, buffer, bufferSize, num);
  	DO_LOG("read_dir result: %#" B_PRIx32 "; num: %" B_PRIu32 "\n", result,
  		*num);
  	return result;
  }
  
  
  static status_t
  overlay_rewind_dir(fs_volume *volume, fs_vnode *vnode, void *cookie)
  {
  	DO_LOG("rewind_dir cookie: %p\n", cookie);
  	OVERLAY_CALL(rewind_dir, cookie)
  	DO_LOG("rewind_dir result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_open_attr_dir(fs_volume *volume, fs_vnode *vnode, void **cookie)
  {
  	DO_LOG("%s\n", "open_attr_dir");
  	OVERLAY_CALL(open_attr_dir, cookie)
  	DO_LOG("open_attr_dir result: %#" B_PRIx32 "; cookie: %p\n", result,
  		*cookie);
  	return result;
  }
  
  
  static status_t
  overlay_close_attr_dir(fs_volume *volume, fs_vnode *vnode, void *cookie)
  {
  	DO_LOG("close_attr_dir cookie: %p\n", cookie);
  	OVERLAY_CALL(close_attr_dir, cookie)
  	DO_LOG("close_attr_dir result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_free_attr_dir_cookie(fs_volume *volume, fs_vnode *vnode, void *cookie)
  {
  	DO_LOG("free_attr_dir_cookie cookie: %p\n", cookie);
  	OVERLAY_CALL(free_attr_dir_cookie, cookie)
  	DO_LOG("free_attr_dir_cookie result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_read_attr_dir(fs_volume *volume, fs_vnode *vnode, void *cookie,
  	struct dirent *buffer, size_t bufferSize, uint32 *num)
  {
  	DO_LOG("read_attr_dir cookie: %p; buffer: %p; buffer_size: %" B_PRIuSIZE
  		"\n", cookie, buffer, bufferSize);
  	OVERLAY_CALL(read_attr_dir, cookie, buffer, bufferSize, num);
  	DO_LOG("read_attr_dir result: %#" B_PRIx32 "; num: %" B_PRIu32 "\n", result,
  		*num);
  	return result;
  }
  
  
  static status_t
  overlay_rewind_attr_dir(fs_volume *volume, fs_vnode *vnode, void *cookie)
  {
  	DO_LOG("rewind_attr_dir cookie: %p\n", cookie);
  	OVERLAY_CALL(rewind_attr_dir, cookie)
  	DO_LOG("rewind_attr_dir result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_create_attr(fs_volume *volume, fs_vnode *vnode, const char *name,
  	uint32 type, int openMode, void **cookie)
  {
  	DO_LOG("create_attr name: \"%s\"; type: %#" B_PRIx32 "; open_mode: %#x\n",
  		name, type, openMode);
  	OVERLAY_CALL(create_attr, name, type, openMode, cookie)
  	DO_LOG("create_attr result: %#" B_PRIx32 "; cookie: %p\n", result, *cookie);
  	return result;
  }
  
  
  static status_t
  overlay_open_attr(fs_volume *volume, fs_vnode *vnode, const char *name,
  	int openMode, void **cookie)
  {
  	DO_LOG("open_attr name: \"%s\"; open_mode: %#x\n", name, openMode);
  	OVERLAY_CALL(open_attr, name, openMode, cookie)
  	DO_LOG("open_attr result: %#" B_PRIx32 "; cookie: %p\n", result, *cookie);
  	return result;
  }
  
  
  static status_t
  overlay_close_attr(fs_volume *volume, fs_vnode *vnode, void *cookie)
  {
  	DO_LOG("close_attr cookie: %p\n", cookie);
  	OVERLAY_CALL(close_attr, cookie)
  	DO_LOG("close_attr result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_free_attr_cookie(fs_volume *volume, fs_vnode *vnode, void *cookie)
  {
  	DO_LOG("free_attr_cookie cookie: %p\n", cookie);
  	OVERLAY_CALL(free_attr_cookie, cookie)
  	DO_LOG("free_attr_cookie result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_read_attr(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos,
  	void *buffer, size_t *length)
  {
  	DO_LOG("read_attr cookie: %p; pos: %" B_PRIdOFF "; buffer: %p; length: %"
  		B_PRIuSIZE "\n", cookie, pos, buffer, *length);
  	OVERLAY_CALL(read_attr, cookie, pos, buffer, length)
  	DO_LOG("read_attr result: %#" B_PRIx32 "; length: %" B_PRIuSIZE "\n",
  		result, *length);
  	return result;
  }
  
  
  static status_t
  overlay_write_attr(fs_volume *volume, fs_vnode *vnode, void *cookie, off_t pos,
  	const void *buffer, size_t *length)
  {
  	DO_LOG("write_attr cookie: %p; pos: %" B_PRIdOFF "; buffer: %p; length: %"
  		B_PRIuSIZE "\n", cookie, pos, buffer, *length);
  	OVERLAY_CALL(write_attr, cookie, pos, buffer, length)
  	DO_LOG("write_attr result: %#" B_PRIx32 "; length: %" B_PRIuSIZE "\n",
  		result, *length);
  	return result;
  }
  
  
  static status_t
  overlay_read_attr_stat(fs_volume *volume, fs_vnode *vnode, void *cookie,
  	struct stat *stat)
  {
  	DO_LOG("read_attr_stat cookie: %p; stat: %p\n", cookie, stat);
  	OVERLAY_CALL(read_attr_stat, cookie, stat)
  	if (result == B_OK) {
  		DO_LOG("read_attr_stat result: %#" B_PRIx32 "; stat(dev: %" B_PRIdDEV
  			"; ino: %" B_PRIdINO "; mode: %#x; uid: %u; gid %u; size: %"
  			B_PRIdOFF "; type: %#" B_PRIx32 ")\n", result,
  			stat->st_dev, stat->st_ino, stat->st_mode, stat->st_uid,
  			stat->st_gid, stat->st_size, stat->st_type);
  	} else
  		DO_LOG("read_attr_stat result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_write_attr_stat(fs_volume *volume, fs_vnode *vnode, void *cookie,
  	const struct stat *stat, int statMask)
  {
  	DO_LOG("write_attr_stat cookie: %p; stat: %p; mask: %#x\n", cookie, stat,
  		statMask);
  	OVERLAY_CALL(write_attr_stat, cookie, stat, statMask)
  	DO_LOG("write_attr_stat result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_rename_attr(fs_volume *volume, fs_vnode *vnode,
  	const char *fromName, fs_vnode *toVnode, const char *toName)
  {
  	DO_LOG("rename_attr from_name: \"%s\"; to_vnode: %p; to_name: \"%s\"\n",
  		fromName, ((fs_vnode *)toVnode->private_node)->private_node, toName);
  	OVERLAY_CALL(rename_attr, fromName, (fs_vnode *)toVnode->private_node,
  		toName)
  	DO_LOG("rename_attr result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_remove_attr(fs_volume *volume, fs_vnode *vnode, const char *name)
  {
  	DO_LOG("remove_attr name: \"%s\"\n", name);
  	OVERLAY_CALL(remove_attr, name)
  	DO_LOG("remove_attr result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_create_special_node(fs_volume *volume, fs_vnode *vnode,
  	const char *name, fs_vnode *subVnode, mode_t mode, uint32 flags,
  	fs_vnode *_superVnode, ino_t *nodeID)
  {
  	DO_LOG("create_special_node name: \"%s\"; sub_vnode: %p; mode: %#x;"
  		" flags: %" B_PRIu32 "\n", name, subVnode->private_node, mode, flags);
  	OVERLAY_CALL(create_special_node, name, (fs_vnode *)subVnode->private_node,
  		mode, flags, _superVnode, nodeID)
  	DO_LOG("create_special_node result: %#" B_PRIx32 "; super_vnode: %p;"
  		" node_id: %" B_PRIdINO "\n", result, _superVnode->private_node,
  		*nodeID);
  	return result;
  }
  
  
  static fs_vnode_ops sOverlayVnodeOps = {
  	&overlay_lookup,
  	&overlay_get_vnode_name,
  
  	&overlay_put_vnode,
  	&overlay_remove_vnode,
  
  	&overlay_can_page,
  	&overlay_read_pages,
  	&overlay_write_pages,
  
  	&overlay_io,
  	&overlay_cancel_io,
  
  	&overlay_get_file_map,
  
  	/* common */
  	&overlay_ioctl,
  	&overlay_set_flags,
  	&overlay_select,
  	&overlay_deselect,
  	&overlay_fsync,
  
  	&overlay_read_symlink,
  	&overlay_create_symlink,
  	&overlay_link,
  	&overlay_unlink,
  	&overlay_rename,
  
  	&overlay_access,
  	&overlay_read_stat,
  	&overlay_write_stat,
  	NULL,	// fs_preallocate
  
  	/* file */
  	&overlay_create,
  	&overlay_open,
  	&overlay_close,
  	&overlay_free_cookie,
  	&overlay_read,
  	&overlay_write,
  
  	/* directory */
  	&overlay_create_dir,
  	&overlay_remove_dir,
  	&overlay_open_dir,
  	&overlay_close_dir,
  	&overlay_free_dir_cookie,
  	&overlay_read_dir,
  	&overlay_rewind_dir,
  
  	/* attribute directory operations */
  	&overlay_open_attr_dir,
  	&overlay_close_attr_dir,
  	&overlay_free_attr_dir_cookie,
  	&overlay_read_attr_dir,
  	&overlay_rewind_attr_dir,
  
  	/* attribute operations */
  	&overlay_create_attr,
  	&overlay_open_attr,
  	&overlay_close_attr,
  	&overlay_free_attr_cookie,
  	&overlay_read_attr,
  	&overlay_write_attr,
  
  	&overlay_read_attr_stat,
  	&overlay_write_attr_stat,
  	&overlay_rename_attr,
  	&overlay_remove_attr,
  
  	/* support for node and FS layers */
  	&overlay_create_special_node,
  	&overlay_get_super_vnode
  };
  
  
  //	#pragma mark - volume ops
  
  
  #define DO_VOLUME_LOG(format, args...) \
  	{ \
  		char _printBuffer[256]; \
  		int _printSize = snprintf(_printBuffer, sizeof(_printBuffer), \
  			"%" B_PRId64 " %" B_PRId32 " %p: " format, system_time(), \
  			find_thread(NULL), volume->super_volume, args); \
  		if ((unsigned int)_printSize > sizeof(_printBuffer)) { \
  			_printBuffer[sizeof(_printBuffer) - 1] = '\n'; \
  			_printSize = sizeof(_printBuffer); \
  		} \
  		write((int)(addr_t)volume->private_volume, _printBuffer, _printSize); \
  	}
  
  #define OVERLAY_VOLUME_CALL(op, params...) \
  	status_t result = B_UNSUPPORTED; \
  	if (volume->super_volume->ops->op != NULL) \
  		result = volume->super_volume->ops->op(volume->super_volume, params);
  
  
  static status_t
  overlay_unmount(fs_volume *volume)
  {
  	if (volume->super_volume != NULL
  		&& volume->super_volume->ops != NULL
  		&& volume->super_volume->ops->unmount != NULL)
  		volume->super_volume->ops->unmount(volume->super_volume);
  
  	close((int)(addr_t)volume->private_volume);
  	return B_OK;
  }
  
  
  static status_t
  overlay_read_fs_info(fs_volume *volume, struct fs_info *info)
  {
  	DO_VOLUME_LOG("%s\n", "read_fs_info");
  	OVERLAY_VOLUME_CALL(read_fs_info, info)
  	DO_VOLUME_LOG("read_fs_info result: %#" B_PRIx32 "; info(dev: %" B_PRIdDEV
  		"; root: %" B_PRIdINO "; flags: %#" B_PRIx32 "; block_size: %" B_PRIdOFF
  		"; io_size: %" B_PRIdOFF "; total_blocks: %" B_PRIdOFF
  		"; free_blocks: %" B_PRIdOFF "; total_nodes: %" B_PRIdOFF
  		"; free_nodes: %" B_PRIdOFF "; device_name: \"%s\"; volume_name: \"%s\""
  		"; fsh_name: \"%s\")\n", result, info->dev, info->root, info->flags,
  		info->block_size, info->io_size, info->total_blocks, info->free_blocks,
  		info->total_nodes, info->free_nodes, info->device_name,
  		info->volume_name, info->fsh_name);
  	return result;
  }
  
  
  static status_t
  overlay_write_fs_info(fs_volume *volume, const struct fs_info *info,
  	uint32 mask)
  {
  	DO_VOLUME_LOG("write_fs_info info: %p; mask: %#" B_PRIx32 "\n", info, mask);
  	OVERLAY_VOLUME_CALL(write_fs_info, info, mask)
  	DO_VOLUME_LOG("write_fs_info result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_sync(fs_volume *volume)
  {
  	DO_VOLUME_LOG("%s\n", "sync");
  	status_t result = B_UNSUPPORTED;
  	if (volume->super_volume->ops->sync != NULL)
  		result = volume->super_volume->ops->sync(volume->super_volume);
  	DO_VOLUME_LOG("sync result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_get_vnode(fs_volume *volume, ino_t id, fs_vnode *vnode, int *type,
  	uint32 *flags, bool reenter)
  {
  	DO_VOLUME_LOG("get_vnode id: %" B_PRIdINO "; vnode: %p; type*: %p;"
  		" flags*: %p; reenter: %s\n", id, vnode, type, flags,
  		reenter ? "yes" : "no");
  
  	if (volume->super_volume->ops->get_vnode == NULL) {
  		DO_VOLUME_LOG("get_vnode %s\n", "not supported");
  		return B_UNSUPPORTED;
  	}
  
  	fs_vnode *superVnode = new(std::nothrow) fs_vnode;
  	if (superVnode == NULL) {
  		DO_VOLUME_LOG("get_vnode %s\n", "no memory");
  		return B_NO_MEMORY;
  	}
  
  	status_t result = volume->super_volume->ops->get_vnode(volume->super_volume,
  		id, superVnode, type, flags, reenter);
  	if (result != B_OK) {
  		DO_VOLUME_LOG("get_vnode result: %#" B_PRIx32 "\n", result);
  		delete superVnode;
  		return result;
  	}
  
  	vnode->private_node = superVnode;
  	vnode->ops = &sOverlayVnodeOps;
  
  	DO_VOLUME_LOG("get_vnode result: %#" B_PRIx32 "; super_vnode: %p\n", result,
  		superVnode->private_node);
  	return B_OK;
  }
  
  
  static status_t
  overlay_open_index_dir(fs_volume *volume, void **cookie)
  {
  	DO_VOLUME_LOG("%s\n", "open_index_dir");
  	OVERLAY_VOLUME_CALL(open_index_dir, cookie)
  	DO_VOLUME_LOG("open_index_dir result: %#" B_PRIx32 "; cookie: %p\n", result,
  		*cookie);
  	return result;
  }
  
  
  static status_t
  overlay_close_index_dir(fs_volume *volume, void *cookie)
  {
  	DO_VOLUME_LOG("close_index_dir cookie: %p\n", cookie);
  	OVERLAY_VOLUME_CALL(close_index_dir, cookie)
  	DO_VOLUME_LOG("close_index_dir result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_free_index_dir_cookie(fs_volume *volume, void *cookie)
  {
  	DO_VOLUME_LOG("free_index_dir_cookie cookie: %p\n", cookie);
  	OVERLAY_VOLUME_CALL(free_index_dir_cookie, cookie)
  	DO_VOLUME_LOG("free_index_dir_cookie result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_read_index_dir(fs_volume *volume, void *cookie, struct dirent *buffer,
  	size_t bufferSize, uint32 *num)
  {
  	DO_VOLUME_LOG("read_index_dir cookie: %p; buffer: %p; buffer_size: %"
  		B_PRIuSIZE "\n", cookie, buffer, bufferSize);
  	OVERLAY_VOLUME_CALL(read_index_dir, cookie, buffer, bufferSize, num)
  	DO_VOLUME_LOG("read_index_dir result: %#" B_PRIx32 "; num: %" B_PRIu32 "\n",
  		result, *num);
  	return result;
  }
  
  
  static status_t
  overlay_rewind_index_dir(fs_volume *volume, void *cookie)
  {
  	DO_VOLUME_LOG("rewind_index_dir cookie: %p\n", cookie);
  	OVERLAY_VOLUME_CALL(rewind_index_dir, cookie)
  	DO_VOLUME_LOG("rewind_index_dir result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_create_index(fs_volume *volume, const char *name, uint32 type,
  	uint32 flags)
  {
  	DO_VOLUME_LOG("create_index name: \"%s\"; type: %#" B_PRIx32
  		"; flags: %#" B_PRIx32 "\n", name, type, flags);
  	OVERLAY_VOLUME_CALL(create_index, name, type, flags)
  	DO_VOLUME_LOG("create_index result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_remove_index(fs_volume *volume, const char *name)
  {
  	DO_VOLUME_LOG("remove_index name: \"%s\"\n", name);
  	OVERLAY_VOLUME_CALL(remove_index, name)
  	DO_VOLUME_LOG("remove_index result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_read_index_stat(fs_volume *volume, const char *name, struct stat *stat)
  {
  	DO_VOLUME_LOG("read_index_stat name: \"%s\"; stat: %p\n", name, stat);
  	OVERLAY_VOLUME_CALL(read_index_stat, name, stat)
  	if (result == B_OK) {
  		DO_VOLUME_LOG("read_index_stat result: %#" B_PRIx32 "; stat(dev: %"
  			B_PRIdDEV "; ino: %" B_PRIdINO "; mode: %#x; uid: %u; gid %u;"
  			" size: %" B_PRIdOFF "; type: %#" B_PRIx32 ")\n", result,
  			stat->st_dev, stat->st_ino, stat->st_mode, stat->st_uid,
  			stat->st_gid, stat->st_size, stat->st_type);
  	} else
  		DO_VOLUME_LOG("read_index_stat result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_open_query(fs_volume *volume, const char *query, uint32 flags,
  	port_id port, uint32 token, void **cookie)
  {
  	DO_VOLUME_LOG("open_query query: \"%s\"; flags: %#" B_PRIx32 "; port: %"
  		B_PRId32 "; token: %" B_PRIu32 "\n", query, flags, port, token);
  	OVERLAY_VOLUME_CALL(open_query, query, flags, port, token, cookie)
  	DO_VOLUME_LOG("open_query result: %#" B_PRIx32 "; cookie: %p\n", result,
  		*cookie);
  	return result;
  }
  
  
  static status_t
  overlay_close_query(fs_volume *volume, void *cookie)
  {
  	DO_VOLUME_LOG("close_query cookie: %p\n", cookie);
  	OVERLAY_VOLUME_CALL(close_query, cookie)
  	DO_VOLUME_LOG("close_query result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_free_query_cookie(fs_volume *volume, void *cookie)
  {
  	DO_VOLUME_LOG("free_query_cookie cookie: %p\n", cookie);
  	OVERLAY_VOLUME_CALL(free_query_cookie, cookie)
  	DO_VOLUME_LOG("free_query_cookie result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_read_query(fs_volume *volume, void *cookie, struct dirent *buffer,
  	size_t bufferSize, uint32 *num)
  {
  	DO_VOLUME_LOG("read_query cookie: %p; buffer: %p; buffer_size: %"
  		B_PRIuSIZE "\n", cookie, buffer, bufferSize);
  	OVERLAY_VOLUME_CALL(read_query, cookie, buffer, bufferSize, num)
  	DO_VOLUME_LOG("read_query result: %#" B_PRIx32 "; num: %" B_PRIu32 "\n",
  		result, *num);
  	return result;
  }
  
  
  static status_t
  overlay_rewind_query(fs_volume *volume, void *cookie)
  {
  	DO_VOLUME_LOG("rewind_query cookie: %p\n", cookie);
  	OVERLAY_VOLUME_CALL(rewind_query, cookie)
  	DO_VOLUME_LOG("rewind_query result: %#" B_PRIx32 "\n", result);
  	return result;
  }
  
  
  static status_t
  overlay_all_layers_mounted(fs_volume *volume)
  {
  	return B_OK;
  }
  
  
  static status_t
  overlay_create_sub_vnode(fs_volume *volume, ino_t id, fs_vnode *vnode)
  {
  	fs_vnode *superVnode = new(std::nothrow) fs_vnode;
  	if (superVnode == NULL)
  		return B_NO_MEMORY;
  
  	*superVnode = *vnode;
  	vnode->private_node = superVnode;
  	vnode->ops = &sOverlayVnodeOps;
  	return B_OK;
  }
  
  
  static status_t
  overlay_delete_sub_vnode(fs_volume *volume, fs_vnode *vnode)
  {
  	delete (fs_vnode *)vnode->private_node;
  	return B_OK;
  }
  
  
  static fs_volume_ops sOverlayVolumeOps = {
  	&overlay_unmount,
  
  	&overlay_read_fs_info,
  	&overlay_write_fs_info,
  	&overlay_sync,
  
  	&overlay_get_vnode,
  	&overlay_open_index_dir,
  	&overlay_close_index_dir,
  	&overlay_free_index_dir_cookie,
  	&overlay_read_index_dir,
  	&overlay_rewind_index_dir,
  
  	&overlay_create_index,
  	&overlay_remove_index,
  	&overlay_read_index_stat,
  
  	&overlay_open_query,
  	&overlay_close_query,
  	&overlay_free_query_cookie,
  	&overlay_read_query,
  	&overlay_rewind_query,
  
  	&overlay_all_layers_mounted,
  	&overlay_create_sub_vnode,
  	&overlay_delete_sub_vnode
  };
  
  
  //	#pragma mark - filesystem module
  
  
  static status_t
  overlay_mount(fs_volume *volume, const char *device, uint32 flags,
  	const char *args, ino_t *rootID)
  {
  	char filename[256];
  	snprintf(filename, sizeof(filename), "%s%s", kLogFilePrefix, device);
  	filename[sizeof(filename) - 1] = 0;
  
  	int filenameLength = strlen(filename);
  	for (int i = strlen(kLogFilePrefix); i < filenameLength; i++) {
  		if (filename[i] == '/')
  			filename[i] = '_';
  	}
  
  	int fd = creat(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
  	if (fd < 0)
  		return errno;
  
  	volume->private_volume = (void *)(addr_t)fd;
  	volume->ops = &sOverlayVolumeOps;
  	return B_OK;
  }
  
  
  static status_t
  overlay_std_ops(int32 op, ...)
  {
  	switch (op) {
  		case B_MODULE_INIT:
  		case B_MODULE_UNINIT:
  			return B_OK;
  		default:
  			return B_ERROR;
  	}
  }
  
  
  static file_system_module_info sOverlayFileSystem = {
  	{
  		"file_systems/log_overlay" B_CURRENT_FS_API_VERSION,
  		0,
  		overlay_std_ops,
  	},
  
  	"log_overlay",						// short_name
  	"Logging Overlay File System",		// pretty_name
  	0,									// DDM flags
  
  	// scanning
  	NULL, // identify_partition
  	NULL, // scan_partition
  	NULL, // free_identify_partition_cookie
  	NULL, // free_partition_content_cookie
  
  	// general operations
  	&overlay_mount,
  
  	// capability querying
  	NULL, // get_supported_operations
  
  	NULL, // validate_resize
  	NULL, // validate_move
  	NULL, // validate_set_content_name
  	NULL, // validate_set_content_parameters
  	NULL, // validate_initialize
  
  	// shadow partition modification
  	NULL, // shadow_changed
  
  	// writing
  	NULL, // defragment
  	NULL, // repair
  	NULL, // resize
  	NULL, // move
  	NULL, // set_content_name
  	NULL, // set_content_parameters
  	NULL // initialize
  };
  
  module_info *modules[] = {
  	(module_info *)&sOverlayFileSystem,
  	NULL,
  };
  