What's new? | Help | Directory | Sign in
Google
macfuse
A User-Space File System Implementation Mechanism for Mac OS X
  
  
  
  
    
Search
for
Updated Jan 16, 2008 by singh
HELLOWORLDFS  
A very simple example file system for MacFUSE

This file contains the source listing for a very simple MacFUSE file system.

Source listing begins next. To use, copy text from here and paste into your source file.

/* For more information, see http://www.macdevcenter.com/pub/a/mac/2007/03/06/macfuse-new-frontiers-in-file-systems.html. */ 
#include <errno.h>
#include <fcntl.h>
#include <string.h>

#define _FILE_OFFSET_BITS 64
#define FUSE_USE_VERSION  26
#include <fuse.h>

static const char  *file_path      = "/hello.txt";
static const char   file_content[] = "Hello World!\n";
static const size_t file_size      = sizeof(file_content)/sizeof(char) - 1;

static int
hello_getattr(const char *path, struct stat *stbuf)
{
    memset(stbuf, 0, sizeof(struct stat));

    if (strcmp(path, "/") == 0) { /* The root directory of our file system. */
        stbuf->st_mode = S_IFDIR | 0755;
        stbuf->st_nlink = 3;
    } else if (strcmp(path, file_path) == 0) { /* The only file we have. */
        stbuf->st_mode = S_IFREG | 0444;
        stbuf->st_nlink = 1;
        stbuf->st_size = file_size;
    } else { /* We reject everything else. */
        return -ENOENT;
    }

    return 0;
}

static int
hello_open(const char *path, struct fuse_file_info *fi)
{
    if (strcmp(path, file_path) != 0) { /* We only recognize one file. */
        return -ENOENT;
    }

    if ((fi->flags & O_ACCMODE) != O_RDONLY) { /* Only reading allowed. */
        return -EACCES;
    }

    return 0;
}

static int
hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
              off_t offset, struct fuse_file_info *fi)
{
    if (strcmp(path, "/") != 0) { /* We only recognize the root directory. */
        return -ENOENT;
    }

    filler(buf, ".", NULL, 0);           /* Current directory (.)  */
    filler(buf, "..", NULL, 0);          /* Parent directory (..)  */
    filler(buf, file_path + 1, NULL, 0); /* The only file we have. */

    return 0;
}

static int
hello_read(const char *path, char *buf, size_t size, off_t offset,
           struct fuse_file_info *fi)
{
    if (strcmp(path, file_path) != 0) {
        return -ENOENT;
    }

    if (offset >= file_size) { /* Trying to read past the end of file. */
        return 0;
    }

    if (offset + size > file_size) { /* Trim the read to the file size. */
        size = file_size - offset;
    }

    memcpy(buf, file_content + offset, size); /* Provide the content. */

    return size;
}

static struct fuse_operations hello_filesystem_operations = {
    .getattr = hello_getattr, /* To provide size, permissions, etc. */
    .open    = hello_open,    /* To enforce read-only access.       */
    .read    = hello_read,    /* To provide file content.           */
    .readdir = hello_readdir, /* To provide directory listing.      */
};

int
main(int argc, char **argv)
{
    return fuse_main(argc, argv, &hello_filesystem_operations, NULL);
}

Comment by hamish, Aug 16, 2007

Here are some additions to HELLOWORLDFS to handle a custom file icon. It has been tested on Leopard with revision 434 of the MacFUSE trunk (the custom icon file works fine, but Quick Look crashes Finder). It doesn't work on 10.4 because Tiger Finder doesn't honour com.apple.{FinderInfo?,ResourceFork?}, only the AppleDouble? format.

Start with the code above, remove the lines defining hello_filesystem_operations and main(), and add the following:

#include <sys/types.h>
#include <sys/mman.h>
#include <stddef.h>

#include "simpleresourcefork.h"

static const char    finder_info_xattr[]      = "com.apple.FinderInfo";
static const char    custom_icon_finder_info[] =
{
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const size_t  finder_info_size         = sizeof(custom_icon_finder_info);

static const char    resource_fork_xattr[]    = "com.apple.ResourceFork";
static const char   *icns_ostype              = "icns";
static const short   finder_icon_resource_id  = -16455;
static void         *resource_fork_content    = 0;
static size_t        resource_fork_size       = 0;

static int
hello_listxattr(const char *path, char *list, size_t size)
{
    if (resource_fork_content == 0)
        return -ENOENT;

    if (strcmp(path, file_path) != 0)
        return -ENOENT;

    if (size > 0)
    {
        memcpy(list, finder_info_xattr, sizeof(finder_info_xattr));
        memcpy(list + sizeof(finder_info_xattr),
            resource_fork_xattr, sizeof(resource_fork_xattr));
    }

    return sizeof(finder_info_xattr) + sizeof(resource_fork_xattr);
}

static int
hello_getxattr(const char *path, const char *name, char *value, size_t size)
{
    if (resource_fork_content == 0)
        return -ENOENT;

    if (strcmp(path, file_path) != 0)
        return -ENOENT;

    if (strcmp(name, finder_info_xattr) == 0)
    {
        if (value != 0 && size >= finder_info_size)
            memcpy(value, custom_icon_finder_info, finder_info_size);
        return finder_info_size;
    }
    else if (strcmp(name, resource_fork_xattr) == 0)
    {
        if (value != 0 && size >= resource_fork_size)
            memcpy(value, resource_fork_content, resource_fork_size);
        return resource_fork_size;
    }
    else return 0;
}

static struct fuse_operations hello_filesystem_operations = {
    .getattr   = hello_getattr,   /* To provide size, permissions, etc.             */
    .open      = hello_open,      /* To enforce read-only access.                   */
    .read      = hello_read,      /* To provide file content.                       */
    .readdir   = hello_readdir,   /* To provide directory listing.                  */
    .listxattr = hello_listxattr, /* To provide com.apple.{FinderInfo,ResourceFork} */
    .getxattr  = hello_getxattr,  /* To provide com.apple.(FinderInfo,ResourceFork) */
};

int
setup_resource_fork(const char *icns_file_name)
{
    int fd = open(icns_file_name, O_RDONLY);
    if (fd < 0)
        return fd;

    struct stat st;
    fstat(fd, &st);

    void *icns_data = mmap(NULL, st.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0);
	if ((int)icns_data == -1)
		return -1;

    rf_rsrc_fork *icns_rsrc = rf_new();
    rf_add(icns_rsrc, icns_ostype, finder_icon_resource_id, icns_data, st.st_size);
    resource_fork_content = rf_export(icns_rsrc, &resource_fork_size);
    rf_free(icns_rsrc);

    return resource_fork_size;
}

struct hellofs { char *icns_file_name; };
static struct hellofs hellofs;
static struct fuse_opt hellofs_opts[] =
{
    { "icns_file=%s", offsetof(struct hellofs, icns_file_name), 0 }
};

int
main(int argc, char **argv)
{
    struct fuse_args args = FUSE_ARGS_INIT(argc, argv);

    if (fuse_opt_parse(&args, &hellofs, hellofs_opts, 0) == -1)
        return -1;

    if (hellofs.icns_file_name != 0)
    { 
        if (setup_resource_fork(hellofs.icns_file_name) < 0)
        {
            fprintf(stderr, "Can't open icns file '%s'\n",
                hellofs.icns_file_name);
            return -1;
        }
        fuse_opt_add_arg(&args, "-onoautoextattr");
    }

    return fuse_main(args.argc, args.argv, &hello_filesystem_operations, NULL);
}

At some point "-onoautoextattr" is probably going to go away, in which case just remove the line that adds it to the arguments.

This code uses a header file simpleresourcefork.h that I wrote using information from http://developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html?:

/*
 *  simpleresourcefork.h
 *
 *  Created by Hamish Allan on 14/08/2007.
 *  Copyright 2007 Hamish Allan. Some rights reserved.
 *  http://creativecommons.org/licenses/by-sa/3.0/
 * 
 *
 */

#ifndef _SIMPLERESOURCEFORK_H
#define _SIMPLERESOURCEFORK_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>


/****
 *
 * shorthand helpers for manipulating multi-byte values
 *
 */

#define RF_U32_FROM(p)    ntohl(*(unsigned long *)(p))
#define RF_U16_FROM(p)    ntohs(*(unsigned short *)(p))
#define RF_S16_FROM(p)    ntohs(*(short *)(p))
#define RF_U8_FROM(p)     (*(unsigned char *)(p))

#define RF_U32_TO(p, v)   *((unsigned long *)(p)) = htonl(v)
#define RF_U16_TO(p, v)   *((unsigned short *)(p)) = htons(v)
#define RF_S16_TO(p, v)   *((short *)(p)) = htons(v)
#define RF_U8_TO(p, v)    *((unsigned char *)(p)) = v


/****
 *
 * sizes and offsets for various parts of resource file format
 *
 */

#define RF_HEADER_FIXEDSIZE                 256
#define RF_HEADER_DATA_OFFSET                 0
#define RF_HEADER_MAP_OFFSET                  4
#define RF_HEADER_DATA_SIZE                   8
#define RF_HEADER_MAP_SIZE                   12

#define RF_DATA_ENTRY_FIXEDSIZE               4
#define RF_DATA_ENTRY_SIZE                    0

#define RF_MAP_FIXEDSIZE                     28
#define RF_MAP_ATTRIBUTES                    22
#define RF_MAP_TYPE_LIST_OFFSET              24
#define RF_MAP_NAME_LIST_OFFSET              26

#define RF_TYPE_LIST_FIXEDSIZE                2
#define RF_TYPE_LIST_ENTRY_COUNT              0

#define RF_TYPE_ENTRY_FIXEDSIZE               8
#define RF_TYPE_ENTRY_TYPE                    0
#define RF_TYPE_ENTRY_REF_COUNT               4
#define RF_TYPE_ENTRY_REF_OFFSET              6

#define RF_REF_ENTRY_FIXEDSIZE               12
#define RF_REF_ENTRY_ID                       0
#define RF_REF_ENTRY_NAME_OFFSET              2
#define RF_REF_ENTRY_ATTRS_AND_DATA_OFFSET    4

#define RF_NAME_ENTRY_FIXEDSIZE               1
#define RF_NAME_ENTRY_SIZE                    0


/****
 *
 * structures for working copy of resource fork
 *
 */

typedef struct rf_ref_link
{
    short id;
    unsigned char attrs;
    unsigned long data_len;
    void *data;
    char *name;
    struct rf_ref_link *next;
}
rf_ref_link;

typedef struct rf_type_link
{
    char ostype[4];
    rf_ref_link *refs;
    struct rf_type_link *next;
}
rf_type_link;

typedef struct
{
    unsigned short attrs;
    rf_type_link *types;
}
rf_rsrc_fork;


/****
 *
 * create a working copy from file format
 *
 */

rf_rsrc_fork *rf_import(void *in)
{
    void *header = in;

    unsigned long data_offset = RF_U32_FROM(header + RF_HEADER_DATA_OFFSET);
    void *data_start = header + data_offset;

    unsigned long map_offset = RF_U32_FROM(header + RF_HEADER_MAP_OFFSET);
    void *map_start = header + map_offset;

    rf_rsrc_fork *out = malloc(sizeof(rf_rsrc_fork));
    out->attrs = RF_U16_FROM(map_start + RF_MAP_ATTRIBUTES);
    out->types = 0;

    unsigned short type_list_offset = RF_U16_FROM(map_start + RF_MAP_TYPE_LIST_OFFSET);
    void *type_list_start = map_start + type_list_offset;
    short type_list_count = RF_S16_FROM(type_list_start + RF_TYPE_LIST_ENTRY_COUNT) + 1;

    unsigned short name_list_offset = RF_U16_FROM(map_start + RF_MAP_NAME_LIST_OFFSET);
    void *name_list_start = map_start + name_list_offset;

    int type_list_index;
    for (type_list_index = 0; type_list_index < type_list_count; ++type_list_index)
    {
        unsigned long type_entry_offset = RF_TYPE_LIST_FIXEDSIZE + (type_list_index * RF_TYPE_ENTRY_FIXEDSIZE);

        void *type_entry = type_list_start + type_entry_offset;
        char *type_code = type_entry + RF_TYPE_ENTRY_TYPE;

        rf_type_link *tlink = malloc(sizeof(rf_type_link));
        memcpy(tlink->ostype, type_code, 4);
        tlink->refs = 0;
        tlink->next = out->types;
        out->types = tlink;

        short ref_list_count = RF_S16_FROM(type_entry + RF_TYPE_ENTRY_REF_COUNT) + 1;
        short ref_list_offset = RF_U16_FROM(type_entry + RF_TYPE_ENTRY_REF_OFFSET);

        int ref_list_index;
        for (ref_list_index = 0; ref_list_index < ref_list_count; ++ref_list_index)
        {
            unsigned long ref_entry_offset = ref_list_offset + (ref_list_index * RF_REF_ENTRY_FIXEDSIZE);
            void *ref_entry = type_list_start + ref_entry_offset;
			unsigned long attrs_and_data_entry_offset;
			memcpy(&attrs_and_data_entry_offset, ref_entry + RF_REF_ENTRY_ATTRS_AND_DATA_OFFSET, 4);
			unsigned char attrs = *(unsigned char *)&attrs_and_data_entry_offset;
			*(unsigned char *)&attrs_and_data_entry_offset = 0;
			unsigned long data_entry_offset = ntohl(attrs_and_data_entry_offset);

            void *data_entry = data_start + data_entry_offset;
            void *data_entry_data = data_entry + RF_DATA_ENTRY_FIXEDSIZE;
            short name_offset = RF_S16_FROM(ref_entry + RF_REF_ENTRY_NAME_OFFSET);

            rf_ref_link *rlink = malloc(sizeof(rf_ref_link));
            rlink->id = RF_S16_FROM(ref_entry + RF_REF_ENTRY_ID);
            rlink->attrs = attrs;
            rlink->data_len = RF_U32_FROM(data_entry + RF_DATA_ENTRY_SIZE);

            rlink->data = malloc(rlink->data_len);
            memcpy(rlink->data, data_entry_data, rlink->data_len);
            if (name_offset > -1)
            {
                void *name_entry = name_list_start + name_offset;
                unsigned char name_size = RF_U8_FROM(name_entry + RF_NAME_ENTRY_SIZE);
                rlink->name = malloc(name_size + 1);
                rlink->name[name_size] = '\0';
            }
            rlink->next = tlink->refs;
            tlink->refs = rlink;
        }
    }
    
    return out;
}


/****
 *
 * create file format from a working copy
 *
 */

void *rf_export(rf_rsrc_fork *in, size_t *fileformat_size)
{
	int data_size = 0, refs_size = 0, names_size = 0;
	int types_size = RF_TYPE_LIST_FIXEDSIZE;

	int num_types = 0;
	rf_type_link *t;
    for (t = in->types; t != 0; t = t->next)
    {
		types_size += RF_TYPE_ENTRY_FIXEDSIZE;
		rf_ref_link *r;
		for (r = t->refs; r != 0; r = r->next)
		{
			data_size += RF_DATA_ENTRY_FIXEDSIZE + r->data_len;
			refs_size += RF_REF_ENTRY_FIXEDSIZE;
			if (r->name)
				names_size += RF_NAME_ENTRY_FIXEDSIZE + strlen(r->name);
		}
		++num_types;
	}
	
	int map_size = RF_MAP_FIXEDSIZE + types_size + refs_size + names_size;
	*fileformat_size = RF_HEADER_FIXEDSIZE + data_size + map_size;

	void *out = malloc(*fileformat_size);
	void *header = out;
	void *data = header + RF_HEADER_FIXEDSIZE;
	void *map = data + data_size;
	void *types = map + RF_MAP_FIXEDSIZE;
	void *refs = types + types_size;
	void *names = refs + refs_size;

	RF_U32_TO(header + RF_HEADER_DATA_OFFSET, RF_HEADER_FIXEDSIZE);
	RF_U32_TO(header + RF_HEADER_MAP_OFFSET, RF_HEADER_FIXEDSIZE + data_size);
	RF_U32_TO(header + RF_HEADER_DATA_SIZE, data_size);
	RF_U32_TO(header + RF_HEADER_MAP_SIZE, map_size);
	RF_U16_TO(map + RF_MAP_ATTRIBUTES, in->attrs);
	RF_U16_TO(map + RF_MAP_TYPE_LIST_OFFSET, types - map);
	RF_U16_TO(map + RF_MAP_NAME_LIST_OFFSET, names - map);
	RF_U16_TO(types + RF_TYPE_LIST_ENTRY_COUNT, num_types - 1);

	void *type_entry = types + RF_TYPE_LIST_FIXEDSIZE;
	void *ref_entry = refs;
	void *name_entry = names;
	void *data_entry = data;

	for (t = in->types; t != 0; t = t->next)
	{
		memcpy(type_entry + RF_TYPE_ENTRY_TYPE, t->ostype, 4);
		RF_U16_TO(type_entry + RF_TYPE_ENTRY_REF_OFFSET, ref_entry - types);

		int num_refs = 0;
		rf_ref_link *r;
		for (r = t->refs; r != 0; r = r->next)
		{
			RF_S16_TO(ref_entry + RF_REF_ENTRY_ID, r->id);
			if (r->name)
			{
				RF_S16_TO(ref_entry + RF_REF_ENTRY_NAME_OFFSET, name_entry - names);
				RF_U8_TO(name_entry, strlen(r->name));
				memcpy(name_entry + RF_NAME_ENTRY_FIXEDSIZE, r->name, strlen(r->name));
				name_entry += RF_NAME_ENTRY_FIXEDSIZE + strlen(r->name);
			}
			else
			{
				RF_S16_TO(ref_entry + RF_REF_ENTRY_NAME_OFFSET, -1);
			}

			unsigned long attr_and_data_entry_offset = htonl(data_entry - data);
			*(unsigned char *)&attr_and_data_entry_offset = r->attrs;
			memcpy(ref_entry + RF_REF_ENTRY_ATTRS_AND_DATA_OFFSET, &attr_and_data_entry_offset, 4);

			RF_U32_TO(data_entry + RF_DATA_ENTRY_SIZE, r->data_len);
			memcpy(data_entry + RF_DATA_ENTRY_FIXEDSIZE, r->data, r->data_len);
			data_entry += RF_DATA_ENTRY_FIXEDSIZE + r->data_len;

			ref_entry += RF_REF_ENTRY_FIXEDSIZE;
			++num_refs;
		}

		RF_U16_TO(type_entry + RF_TYPE_ENTRY_REF_COUNT, num_refs - 1);
		type_entry += RF_TYPE_ENTRY_FIXEDSIZE;
	}

	return out;
}


/****
 *
 * create blank working copy
 *
 */

rf_rsrc_fork *rf_new()
{
	return calloc(sizeof(rf_rsrc_fork), 0);
}


/****
 *
 * add item to working copy
 *
 */

void rf_add(rf_rsrc_fork *inout, const char *new_type, short new_id, void *new_data_item, size_t new_data_item_size)
{
	rf_type_link *t, *found_t = 0;
	for (t = inout->types; t != 0; t = t->next)
	{
		if (memcmp(t->ostype, new_type, 4) == 0)
		{
			found_t = t;
			break;
		}
	}

	if (!found_t)
	{
        found_t = malloc(sizeof(rf_type_link));
        memcpy(found_t->ostype, new_type, 4);
        found_t->refs = 0;
		found_t->next = inout->types;
		inout->types = found_t;
	}

	rf_ref_link *r, *found_r = 0;
	for (r = found_t->refs; r != 0; r = r->next)
	{
		if (r->id == new_id)
		{
			found_r = r;
			break;
		}
	}

	if (found_r)
	{
		free(found_r->data);
	}
	else
	{
		found_r = malloc(sizeof(rf_ref_link));
		found_r->id = new_id;
		found_r->attrs = 0;
		found_r->name = 0;
		found_r->next = found_t->refs;
		found_t->refs = found_r;
	}

	found_r->data_len = new_data_item_size;
    found_r->data = malloc(new_data_item_size);
    memcpy(found_r->data, new_data_item, new_data_item_size);
}


/****
 *
 * free up the bits we've malloced
 *
 */

void rf_free(rf_rsrc_fork *in)
{
    rf_type_link *t = in->types;
    while (t != 0)
    {
        rf_ref_link *r = t->refs;
        while (r != 0)
        {
            if (r->data)
                free(r->data);
            if (r->name)
                free(r->name);
            rf_ref_link *free_r = r;
            r = r->next;
            free(free_r);
        }
        rf_type_link *free_t = t;
        t = t->next;
        free(free_t);
    }
    free(in);
}


/****
 *
 * dump contents of working copy for debug purposes
 *
 */

void rf_dump(rf_rsrc_fork *in)
{
    printf("resource attrs %d\n", in->attrs);
    rf_type_link *t;
    for (t = in->types; t != 0; t = t->next)
    {
        printf("    type %c%c%c%c\n", t->ostype[0], t->ostype[1], t->ostype[2], t->ostype[3]);
		rf_ref_link *r;
		for (r = t->refs; r != 0; r = r->next)
		{
			printf("        ref id %d attrs %d name %s length %d\n", r->id, r->attrs, r->name, r->data_len);
		}
    }
	printf("\n");
}


#endif // ifndef _SIMPLERESOURCEFORK_H