SvnFs has gotten to somewhat of a milestone! You can use it to mount a subversion repository, and browse it like a filesystem – ie. you can use cd and ls to look around the repository. There’s no auth support, so it’s pretty much only useful for anonymous or otherwise authenticated (eg. svn+ssh, file) repositories.
While the FUSE documentation isn’t great, the example programs are very easy to follow. In this case, the setup is the same as the hello.c setup.
#include <fuse.h>
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
fuse_opt_parse(&args, &svnfs, svnfs_opts, svnfs_parse_opts);
return(fuse_main(args.argc, args.argv, &svnfs_oper));
Here, I’m using svnfs as a global struct to hold specific SvnFs options (eg. debugging on or off); svnfs_opts are the possible command line options and svnfs_parse_opts is the function to parse SvnFs specific options.
To get started with Subversion, the first thing I needed was a client context and a memory pool. Subversion uses APR for portability. APR manages the allocation and deallocation (along with plenty of other stuff) of memory for you, and it uses a concept of pools of memory. So, to start with, I created a global memory pool, and I initialise it in a function called from main().
#include <apr_pools.h>
apr_pool_t *pool;
apr_initialize();
pool = svn_pool_create(pool);
Secondly, I created the client context.
#include <svn_client.h>
svn_client_ctx_t *ctx;
svn_auth_baton_t *auth_baton;
apr_array_header_t *providers;
svn_auth_provider_object_t *username_wc_provider;svn_client_create_context(&ctx, pool);
svn_config_get_config(&(ctx->config), NULL, pool);providers = apr_array_make(pool, 1, sizeof(svn_auth_provider_object_t *));
username_wc_provider = apr_pcalloc(pool, sizeof(*username_wc_provider));
svn_client_get_username_provider(&username_wc_provider, pool);
*(svn_auth_provider_object_t **)apr_array_push(providers) = username_wc_provider;
svn_auth_open(&auth_baton, providers, pool);
ctx->auth_baton = auth_baton;
This block of code loads up your configuration (currently not supported, the NULL in svn_config_get_config is the configuration file name), and sets up the authentication baton for the client context.
Lastly, I use a recursive svn ls to get the full list of files from the repository, and store this in a linked list. Getting the list of files from Subversion is done as follows.
apr_hash_t *dirents;
apr_array_header_t *array;
svn_opt_revision_t *rev = { 0 };
svn_sort__item_t *item;
svn_dirent_t *dirent;
char *fullpath;SVN_ERR(svn_client_ls(&dirents, fullpath, rev, TRUE, ctx, pool));
array = svn_sort__hash(dirents, svn_sort_compare_items_as_paths, pool);for( int i = 0; i < array->nelts; ++i ) {
item = &APR_ARRAY_IDX(array, i, svn_sort__item_t);
utf8_name = item->key;dirent = apr_hash_get(dirents, utf8_name, item->klen);
}
I populate my linked list with the filename and a struct stat for each file. The struct stat is only used (at the moment) to store the mode – 0755 for directories, 0644 for files. Obviously, this will have to be updated in future to match real file permissions, or preferred permissions (eg. if you wanted to use SvnFs to store your email in Subversion, you don’t want world readable files).
The svn_dirent_t *dirent; above has a member called kind which can be svn_node_file or svn_node_dir. This is what I’m using to set the mode of the file.
Lastly, when a readdir() is called, svnfs_readdir get’s called. This simply returns all the elements in the current directory, but checking the filename in the linked list, and omitting any files in subdirectories. When getattr() is called, svnfs_getattr() gets run. This finds the entry in the linked list that matches the filename being looked for, and returns the struct stat for that element. This search needs to be optimised for larger repositories, but for now, it works! Pretty simple really, but functional!

