DESCRIPTION fuse_tcmur provides access to tcmu-runner devices through a fuse(8) mount. Each tcmu-runner device is represented by a node in the fuse filesystem tree. The devices can be accessed via the nodes, for example with dd(1), and can be mounted as filesystems. The diagram shows the components of the fuse_tcmur program (to the right of kernel). fuse_tcmur sits in the middle, providing a main program and the translation between fuse operations and tcmu-runner handler operations. fuse_tree is a simple tree-structure implemented on libfuse. Like /proc, the existence of nodes in the tree is controlled only by the server, not through filesystem operations. But individual leaf nodes may be readable and/or writable through the filesystem depending on permissions. .--fuse_operations | /tcmur <==> kernel <==> libfuse <==> fuse_tree ^ | -errno | | fuse_node_ops \V fuse_tcmur ^ | cmd->done | | calls to tcmur_* | V libtcmur <==> tcmur-handler tcmur-handler is one of the binaries in /usr/local/lib/tcmu-runner/. libtcmur is a usermode API to the loadable tcmu-runner handlers. LIO, TCMU, and tcmur-runner are uninvolved -- libtcmur only loads and uses the handlers, and only the block-I/O entry points are called (not handle_cmd). fuse_tree(3) and libtcmur(3) are independent -- each usable for its purpose without reference to the other. In the middle is fuse_tcmur, which links them together, and consists of three parts: The main() program initializes the other parts and calls fuse_main(). The fuse_tcmur part translates I/O requests between fuse and libtcmur. The fuse_tcmur_ctl part interprets commands written to a node in the fuse filesystem -- commands can be written with cat(1) or echo(1) to /tcmur/dev/tcmur. FILES When a handler is loaded, an empty directory node appears for it in /tcmur/sys/modules When a device is added, a node appears in /tcmur/dev/<subtype><minornumber> The /tcmur/dev nodes appear as regular files (rather than block devices), but they can still be mounted as filesystems, e.g. sudo mount /tcmur/dev/ram000 /mnt/k NOTES The source is in the libtcmur branch (default) at https://github.com/DavidButterfield/tcmu-runner.git First make in the main tcmu-runner directory, to get version.h and the binary handlers: cmake . make sudo make install Then cd into the libtcmur subdirectory where there is a hacked-up makefile that creates the fuse_tcmur binary. Today there are no dependencies computed, so always make clean; make The program will attempt to create the mount-point directory that fuse will mount on. This will succeed if the program is run as superuser; otherwise you can create it manually first with sudo mkdir /tcmur The server presently runs only one thread. Despite this, the time for a script to download a few repositories and build a software package is only a few percent longer through the fuse mount to a tcmu-runner ramdisk, as compared with a regular kernel filesystem mount to my home directory spinning disk. (Not an optimal comparison, but it's what I have at hand.) BUGS No doubt. This code was born in July 2019. The makefile leaves much to be desired. So far I have only tested it using handler_ram.so, because that's the handler that can be used without figuring out how to install and run sophisticated back-end software. SEE ALSO fuse_tree(3), libtcmur(3), fuse(8), tcmu-runner(8) AUTHOR David A. Butterfield Manpage updated 23 Jul 2019
fuse_tcmur(1) User Commands fuse_tcmur(1) NAME fuse_tcmur - access tcmu-runner devices through a fuse mount SYNOPSIS fuse_tcmur echo "command" > /tcmur/dev/tcmur Commands: load subtype # load tcmu-runner handler for subtype unload subtype add minornumber /subtype/handler-cfg-string remove minornumber help source command-file-name # read commands from file dump # dump the fuse tree structure DESCRIPTION fuse_tcmur provides access to tcmu-runner devices through a fuse(8) mount. Each tcmu-runner device is represented by a node in the fuse filesystem tree. The devices can be accessed via the nodes, for example with dd(1), and can be mounted as filesystems. Each tcmu device is denoted by a unique minor number, which is specified when the device is added. All tcmu-runner handlers share one minor-space. The diagram shows the components of the fuse_tcmur program (to the right of kernel). fuse_tcmur sits in the middle, providing a main program and the translation between fuse operations and tcmu-runner handler operations. fuse_tree is a simple tree-structure implemented on libfuse. Like /proc, the existence of nodes in the tree is controlled only by the server, not through filesystem operations. But individual leaf nodes may be readable and/or writable through the filesystem depending on permissions. .--fuse_operations | /tcmur <==> kernel <==> libfuse <==> fuse_tree ^ | -errno | | fuse_node_ops \V fuse_tcmur ^ | cmd->done | | calls to tcmur_* | V libtcmur <==> tcmur-handler tcmur-handler is one of the binaries in /usr/local/lib/tcmu-runner/. libtcmur is a usermode API to the loadable tcmu-runner handlers. LIO, TCMU, and tcmur-runner are uninvolved -- libtcmur only loads and uses the handlers, and only the block-I/O entry points are called (not handle_cmd). fuse_tree(3) and libtcmur(3) are independent -- each usable for its purpose without reference to the other. In the middle is fuse_tcmur, which links them together, and consists of three parts: The main() program initializes the other parts and calls fuse_main(). The fuse_tcmur part translates I/O requests between fuse and libtcmur. The fuse_tcmur_ctl part interprets commands written to a node in the fuse filesystem -- commands can be written with cat(1) or echo(1) to /tcmur/dev/tcmur. FILES When a handler is loaded, an empty directory node appears for it in /tcmur/sys/modules When a device is added, a node appears in /tcmur/dev/<subtype><minornumber> The /tcmur/dev nodes appear as regular files (rather than block devices), but they can still be mounted as filesystems, e.g. sudo mount /tcmur/dev/ram000 /mnt/k NOTES The source is in the libtcmur branch (default) at https://github.com/DavidButterfield/tcmu-runner.git First make in the main tcmu-runner directory, to get version.h and the binary handlers: cmake . make sudo make install Then cd into the libtcmur subdirectory where there is a hacked-up makefile that creates the fuse_tcmur binary. Today there are no dependencies computed, so always make clean; make The program will attempt to create the mount-point directory that fuse will mount on. This will succeed if the program is run as superuser; otherwise you can create it manually first with sudo mkdir /tcmur The server presently runs only one thread. Despite this, the time for a script to download a few repositories and build a software package is only a few percent longer through the fuse mount to a tcmu-runner ramdisk, as compared with a regular kernel filesystem mount to my home directory spinning disk. (Not an optimal comparison, but it's what I have at hand.) BUGS No doubt. This code was born in July 2019. The makefile leaves much to be desired. So far I have only tested it using handler_ram.so, because that's the handler that can be used without figuring out how to install and run sophisticated back-end software. SEE ALSO fuse_tree(3), libtcmur(3), fuse(8), tcmu-runner(8) AUTHOR David A. Butterfield Manpage updated 23 Jul 2019
fuse_tree(3) Linux Programmer's Manual fuse_tree(3) NAME fuse_tree -- API to fuse filesystem tree SYNOPSIS #include "fuse_tree.h" error_t fuse_tree_init(const char * mountpoint); error_t fuse_tree_exit(void); error_t fuse_loop_run(void * unused); fuse_node_t fuse_node_add( const char * name, fuse_node_t parent, mode_t, const struct fuse_node_ops *, uintptr_t data); error_t fuse_node_remove(const char * name , fuse_node_t parent); fuse_node_t fuse_tree_mkdir(const char * name, fuse_node_t parent); error_t fuse_tree_rmdir(const char * name, fuse_node_t parent); fuse_node_t fuse_node_lookup(const char * path); uintptr_t fuse_node_data_get(fuse_node_t); void fuse_node_update_mode(fuse_node_t, mode_t); void fuse_node_update_size(fuse_node_t, size_t); void fuse_node_update_mtime(fuse_node_t); char * fuse_tree_fmt(void); DESCRIPTION fuse_tree maintains a filesystem tree, implementing fuse_operations. Like /proc, the tree itself is managed internally by the application -- there is no creation of files or directories through system calls on the mounted fuse filesystem. However, also like /proc, individual files represented in the tree may be readable and/or writable through the mounted filesystem, depending on permissions. When adding a node to the tree, the application can supply a fuse_node_ops vector specifying functions to be called to back filesystem operations on the node. The struct fuse_node_ops includes these members, all of which are optional to fill in: int (*open) (fuse_node_t, uintptr_t data); int (*release)(fuse_node_t, uintptr_t data); int (*fsync) (uintptr_t data, int datasync); ssize_t (*read) (uintptr_t data, void * buf, size_t, loff_t); ssize_t (*write) (uintptr_t data, const char * buf, size_t, loff_t); fuse_tree_init() should be called before any of the other calls described here, passing the path to the mount point to be used for the fuse mount. fuse_tree_exit() should be called last after any other calls described here. fuse_loop_run() should be called to run fuse_main(). fuse_node_add() adds a node with the given name under the given parent node. fuse_node_remove() removes it. The last "data" argument is private to the caller and is passed to the fuse_node_ops callback functions. fuse_tree_mkdir() creates a new child directory fuse_node with the specified name under the specified parent fuse_node. fuse_tree_rmdir() removes it. fuse_node_lookup() returns a pointer to the fuse_node representing the path string. Path string is the full path from the fuse mount, starting with '/'. fuse_node_data_get() returns the private data specified to fuse_node_add. fuse_node_update_mode() updates the fuse_node's mode permissions. fuse_node_update_size() updates the fuse_node's size in bytes. fuse_node_update_mtime() updates the fuse_node's modification time to the present. fuse_tree_fmt() returns a human-readable string representing the fuse tree. The string should be freed by the caller when done with it. RETURN VALUE Upon successful completion, functions returning type error_t return zero. Failures return -errno. fuse_node_add() and fuse_tree_mkdir() each return a pointer to the new fuse_node, or NULL on error. fuse_node_lookup() returns a pointer to the fuse_node, or NULL if the path is not found. fuse_tree_fmt() returns a freeable human-readable debugging string representing the fuse tree. ERRORS fnode_remove() -EBUSY fuse_node is open by some process through the fuse FS -ENOENT named fnode not found under parent fuse_tree_rmdir() -ENOENT named fnode not found under parent -ENOTEMPTY directory node is not empty fuse_tree_init() -EINVAL mountpoint does not start with '/', or it ends in '/' fuse_tree_exit() -EBUSY root node still has child(ren) fuse_loop_run() errors returned by fuse_main() errors returned by asprintf() NOTES BUGS fuse_node_lookup() should hold the returned node, and a new function should be added to drop it. There should be a threading option passed to fuse_loop_run(). At present it always runs fuse single-threaded. SEE ALSO fuse(8) AUTHOR David A. Butterfield Manpage updated 23 Jul 2019
libtcmur(3) Linux Programmer's Manual libtcmur(3) NAME libtcmur -- usermode API to tcmu-runner block storage handlers SYNOPSIS #include "libtcmur.h" error_t libtcmur_init(const char * handler_prefix); error_t libtcmur_exit(void); error_t tcmur_handler_load(const char * subtype); error_t tcmur_handler_unload(const char * subtype); error_t tcmur_check_config(char const * cfgstring); error_t tcmur_device_add(int minor, const char * cfgstring); error_t tcmur_device_remove(int minor); error_t tcmur_read(int minor, struct tcmulib_cmd *, struct iovec *, size_t niov, size_t, loff_t); error_t tcmur_write(int minor, struct tcmulib_cmd *, struct iovec *, size_t niov, size_t, loff_t); error_t tcmur_flush(int minor, struct tcmulib_cmd *); ssize_t tcmur_get_size(int minor); ssize_t tcmur_get_block_size(int minor); ssize_t tcmur_get_max_xfer(int minor); const char * tcmur_get_dev_name(int minor); DESCRIPTION libtcmur provides a usermode application programming interface to access block storage services through tcmu-runner block storage handlers. Note that LIO, TCMU and tcmu-runner are uninvolved -- libtcmur only calls the tcmu-runner loadable storage handlers, e.g. qcow, glfs, ram, etc. libtcmur makes use of the handler read, write, and flush block I/O functions only -- no calls are made to handle_cmd(). Functions returning type error_t return zero for success, otherwise -errno. Call libtcmur_init() once before using libtcmur services. If handler_prefix is NULL, the default is used: "/usr/local/lib/tcmu-runner/handler_". The expected handler paths are the concatenation of: handler_prefix tcmu_subtype ".so" Call libtcmur_exit() once last, after any other functions described here. tcmur_handler_load() will dlopen() a tcmu-runner handler of the given subtype and load it into the program for use. tcmur_handler_unload() unloads it. tcmur_check_config() checks a handler device configuration string for validity. The handler for the configuration must already have been loaded using tcmur_handler_load(). cfgstring takes this form, specifying the handler's TCMU subtype: /subtype/handler-cfg-string See tcmu-runner(8) for more about subtype and handler-cfg-string. tcmur_device_add() adds a device, with specified cfgstring, as the specified tcmur minor number. tcmur_device_remove() removes the specified minor. The handler is determined from the subtype in the first segment of cfgstring. All tcmur subtypes share a common space of minor numbers. tcmur_read(), tcmur_write() and tcmur_flush() start I/O operations to the specified minor. Errors in the I/O start process can be reported by -errno return from these calls. A return value of zero denotes a successful I/O start, in which case there will be a completion call to cmd->done(), which may report either an "sts" error, or success (denoted by TCMU_STS_OK). Note that the completion call may occur before the request call returns. tcmur_read() and tcmur_write() take an iovec array with niov elements, an I/O size in bytes, and a seek offset into the device where the I/O begins. struct tcmulib_cmd includes this field, which must be set before passing the command to tcmur_read(), tcmur_write(), or tcmur_flush(): cmd_done_t done; /* completion callback */ The callback function is of this type: typedef void (*cmd_done_t)(struct tcmu_device *, struct tcmulib_cmd *, int); The third argument to the callback is TCMU status (see libtcmu_common.h). tcmur_get_size() returns the size in bytes of the specified minor. If the minor does not exist then the return is a -errno. tcmur_get_block_size() returns the block size in bytes of the specified minor. If the minor does not exist then the return is a -errno. tcmur_get_max_xfer() returns the maximum I/O size in bytes of the specified minor. If the minor does not exist then the return is a -errno. tcmur_get_dev_name() returns the device name of the specified minor. If the minor does not exist then the return is NULL. RETURN VALUE Upon successful completion, functions returning type error_t return zero. All functions return -errno on failure, except tcmur_get_dev_name(), which returns NULL in that case. ERRORS tcmur_read() tcmur_write() -ENODEV no device at specified minor (including minor out of range) -ENXIO handler does not implement the requested function -EINVAL I/O would exceed device bounds -EIO I/O completed with nonzero "sts" tcmur_flush() -ENODEV no device at specified minor -EIO I/O completed with nonzero "sts" tcmur_get_size(), tcmur_get_block_size(), tcmur_get_max_xfer() -ENODEV no device at specified minor tcmur_device_add() -ENODEV minor number out of range -EBUSY minor number already in use by prior add errors returned by tcmur_check_config() errors returned by rhandler->open() tcmur_device_remove() -ENODEV no device at specified minor tcmur_check_config() -ENXIO no loaded handler subtype matches this config string -EINVAL config string does not start with '/' -EINVAL config string is too long errors returned by rhandler->check_config() tcmur_handler_load() -EEXIST handler already loaded for the specified subtype -ENOSPC all handler slots are in use -ENOMEM failed to asprintf the handler path -ENOENT failed to dlopen the handler path -EBADF failed to dlsym("handler_init") -EIO handler_init returned non-zero tcmur_handler_unload() -ENOENT no handler is loaded for the specified subtype -EBUSY handler has existing devices (added but not removed) libtcmur_exit() -EBUSY a handler is still loaded NOTES BUGS Not all symbols possibly referenced by handlers are implemented. Some such symbols are "stubbed out" and print a warning if called. Others do not exist, which will disallow loading of referencing handlers. There is no check for an attempt to add the same device twice. There is no generic way to specify the device size, block size, or maximum I/O size. libtcmur_exit() should auto-unload any handlers that have no devices currently added. SEE ALSO tcmu-runner(8) AUTHOR David A. Butterfield Manpage updated 23 Jul 2019