Prepare Makefile and README for release 0.5.0
[omfs_fuse.git] / main.c
blob167bb62e2612383dd4cf157332448f151f7972b3
1 /*
2 * OMFS in Fuse
3 * (c) 2007 Bob Copeland
4 * Released under GPL
5 */
6 #include <errno.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <assert.h>
10 #include <fuse.h>
11 #include <glib.h>
12 #include <pthread.h>
13 #include "omfs.h"
15 static omfs_info_t omfs_info;
16 static GHashTable *inode_cache;
17 static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
19 #define min(a,b) ((a)<(b)?(a):(b))
21 struct inode_ref
23 int refcount;
24 omfs_inode_t *inode;
27 static inline void set_handle(struct fuse_file_info *fi, omfs_inode_t *inode)
29 fi->fh = (long) inode;
32 static inline omfs_inode_t *get_handle(struct fuse_file_info *fi)
34 return (omfs_inode_t *) (long) fi->fh;
37 static guint inode_cache_hash(gconstpointer key)
39 return (int) *((u64*) key);
42 static gboolean inode_cache_compare(gconstpointer key, gconstpointer key2)
44 return *((u64*)key) == *((u64*) key2);
47 static void cache_save_inode(omfs_inode_t *inode)
49 omfs_write_inode(&omfs_info, inode);
52 static void cache_put_inode(omfs_inode_t *inode)
54 struct inode_ref *ref;
56 pthread_mutex_lock(&cache_mutex);
57 ref = g_hash_table_lookup(inode_cache, &inode->head.self);
58 if (!ref)
59 goto out;
61 ref->refcount--;
62 if (!ref->refcount)
64 g_hash_table_remove(inode_cache, &inode->head.self);
65 omfs_release_inode(inode);
66 free(ref);
68 out:
69 pthread_mutex_unlock(&cache_mutex);
72 /**
73 * Called wth cache lock held.
75 static struct inode_ref *cache_add_new_entry(omfs_inode_t *inode)
77 struct inode_ref *ref;
79 ref = malloc(sizeof(struct inode_ref));
81 ref->refcount = 0;
82 ref->inode = inode;
83 g_hash_table_replace(inode_cache, &inode->head.self, ref);
85 return ref;
88 static omfs_inode_t *cache_get_inode(u64 ino)
90 struct inode_ref *ref;
91 omfs_inode_t *inode = NULL;
92 u64 tmp = swap_be64(ino);
94 pthread_mutex_lock(&cache_mutex);
95 ref = g_hash_table_lookup(inode_cache, &tmp);
96 if (!ref)
98 inode = omfs_get_inode(&omfs_info, ino);
99 if (!inode)
100 goto out;
102 ref = cache_add_new_entry(inode);
103 if (!ref)
104 goto out;
106 ref->refcount++;
107 inode = ref->inode;
108 out:
109 pthread_mutex_unlock(&cache_mutex);
110 return inode;
113 static omfs_inode_t *cache_new_inode(omfs_info_t *info, u64 block, char *name,
114 char type)
116 omfs_inode_t *new_inode = omfs_new_inode(info, block, name, type);
117 if (!new_inode)
118 return NULL;
120 pthread_mutex_lock(&cache_mutex);
121 cache_add_new_entry(new_inode);
122 pthread_mutex_unlock(&cache_mutex);
123 return new_inode;
127 static omfs_inode_t *omfs_find_by_name(omfs_inode_t *parent, char *name)
129 u64 next;
130 omfs_inode_t *inode;
131 u64 *chain_ptr = (u64*) ((u8*) parent + OMFS_DIR_START);
133 chain_ptr += omfs_compute_hash(&omfs_info, name);
134 next = swap_be64(*chain_ptr);
135 while (next != ~0)
137 inode = cache_get_inode(next);
138 if (!inode)
139 goto out;
141 if (strcmp(inode->name,name) == 0)
142 break;
144 next = swap_be64(inode->sibling);
145 cache_put_inode(inode);
148 if (next == ~0)
149 inode = NULL;
150 out:
151 return inode;
155 * Split path into directory and filename portions.
156 * Caller must free the returned pointer.
158 static char *split_path(const char *path, char **basename, char **dirname)
160 char *p, *dir, *file;
162 p = strdup(path);
163 if (!p)
164 return NULL;
166 dir = p;
167 file = strrchr(p, '/');
169 if (file) {
170 *file++ = 0;
171 } else {
172 file = p;
173 dir = file + strlen(file);
175 *basename = dir;
176 *dirname = file;
177 return p;
180 static omfs_inode_t *omfs_lookup(const char *path)
182 char *tmp, *p, *save_ptr;
183 omfs_inode_t *inode = NULL, *tmp_inode;
185 // starting at the root, find the inode of path
186 if (path[0] == '/')
187 path++;
189 p = strdup(path);
190 if (!p)
191 return NULL;
193 inode = cache_get_inode(swap_be64(omfs_info.root->root_dir));
195 tmp = strtok_r(p, "/", &save_ptr);
196 while (tmp && inode)
198 tmp_inode = omfs_find_by_name(inode, tmp);
199 cache_put_inode(inode);
200 inode = tmp_inode;
201 tmp = strtok_r(NULL, "/", &save_ptr);
203 free(p);
204 return inode;
207 static int omfs_getattr(const char *path, struct stat *stbuf)
209 u64 ctime;
210 struct fuse_context *ctx;
212 omfs_inode_t *inode = omfs_lookup(path);
213 if (!inode)
214 return -ENOENT;
216 ctx = fuse_get_context();
217 memset(stbuf, 0, sizeof (struct stat));
218 if (inode->type == OMFS_DIR) {
219 stbuf->st_mode = S_IFDIR | 0755;
220 stbuf->st_nlink = 2;
221 } else {
222 stbuf->st_mode = S_IFREG | 0644;
223 stbuf->st_nlink = 1;
224 stbuf->st_size = swap_be64(inode->size);
227 ctime = swap_be64(inode->ctime) / 1000L;
228 stbuf->st_ctime = stbuf->st_mtime = ctime;
229 stbuf->st_uid = ctx->uid;
230 stbuf->st_gid = ctx->gid;
232 return 0;
235 static int omfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
236 off_t offset, struct fuse_file_info *fi)
238 int num_entries, i;
239 u64 *ptr;
240 omfs_inode_t *dir, *tmp;
242 dir = omfs_lookup(path);
243 if (!dir)
244 return -ENOENT;
246 filler(buf, ".", NULL, 0);
247 filler(buf, "..", NULL, 0);
249 num_entries = (swap_be32(dir->head.body_size) +
250 sizeof(omfs_header_t) - OMFS_DIR_START) / 8;
251 ptr = (u64*) ((u8*) dir + OMFS_DIR_START);
253 for (i=0; i<num_entries; i++, ptr++)
255 u64 inum = swap_be64(*ptr);
256 while (inum != ~0)
258 tmp = cache_get_inode(inum);
259 if (!tmp)
260 return -ENOENT;
262 filler(buf, tmp->name, NULL, 0);
263 inum = swap_be64(tmp->sibling);
265 cache_put_inode(tmp);
268 return 0;
271 static int _add_inode(const char *path, char type)
273 char *dir, *file, *tmp;
274 u64 block, *table;
275 omfs_inode_t *parent, *new_inode;
276 int hash;
277 int err = 0;
279 tmp = split_path(path, &dir, &file);
280 if (!tmp)
281 return -ENOMEM;
283 parent = omfs_lookup(dir);
284 if (!parent) {
285 err = -ENOENT;
286 goto out2;
289 err = omfs_allocate_block(&omfs_info, swap_be32(omfs_info.super->mirrors),
290 &block);
291 if (err)
292 goto out1;
294 new_inode = cache_new_inode(&omfs_info, block, file, type);
295 if (!new_inode) {
296 err = -ENOMEM;
297 goto out1;
300 table = (u64*) ((u8*) parent + OMFS_DIR_START);
301 hash = omfs_compute_hash(&omfs_info, file);
303 new_inode->parent = parent->head.self;
304 new_inode->sibling = table[hash];
305 table[hash] = swap_be64(block);
307 cache_save_inode(new_inode);
308 cache_save_inode(parent);
310 out1:
311 out2:
312 free(tmp);
313 return err;
316 static int omfs_mknod(const char *path, mode_t mode, dev_t dev)
318 return _add_inode(path, OMFS_FILE);
321 static int omfs_mkdir(const char *path, mode_t mode)
323 return _add_inode(path, OMFS_DIR);
326 static int omfs_open (const char *path, struct fuse_file_info *fi)
328 omfs_inode_t *inode = omfs_lookup(path);
330 if (!inode)
331 return -ENOENT;
333 set_handle(fi, inode);
334 return 0;
337 static u64 find_block(struct omfs_extent_entry **entry, u64 block, int count)
339 u64 searched = 0;
340 for (; count > 1; count--)
342 u64 numblocks = swap_be64((*entry)->blocks);
343 if (block >= searched && block < searched + numblocks)
344 return swap_be64((*entry)->cluster) + block - searched;
346 searched += numblocks;
347 (*entry)++;
349 return 0;
353 * Given an offset into a file, find the extent table and entry
354 * containing the location. Return the fs block of the location.
356 static u64 omfs_find_location(u64 requested, omfs_inode_t *inode,
357 struct omfs_extent **ret_oe, struct omfs_extent_entry **ret_entry)
359 struct omfs_extent_entry *entry;
360 struct omfs_extent *oe;
361 u64 found_block = 0;
363 oe = (struct omfs_extent *) ((u8*) inode + OMFS_EXTENT_START);
365 for (;;)
367 int extent_count = swap_be32(oe->extent_count);
368 u64 next = swap_be64(oe->next);
369 entry = &oe->entry;
371 found_block = find_block(&entry, requested, extent_count);
372 if (found_block != 0)
373 goto out;
375 if (next == ~0)
376 break;
378 inode = cache_get_inode(next);
379 oe = (struct omfs_extent *) ((u8*) inode + OMFS_EXTENT_CONT);
381 out:
382 *ret_entry = entry;
383 *ret_oe = oe;
384 return found_block;
387 static u8 *omfs_get_data_n(u64 requested, struct fuse_file_info *fi)
389 struct omfs_extent *oe;
390 struct omfs_extent_entry *entry;
392 omfs_inode_t *inode = get_handle(fi);
394 u64 block = omfs_find_location(requested, inode, &oe, &entry);
396 if (block != 0)
397 return omfs_get_block(&omfs_info, block);
399 return NULL;
402 static int omfs_read (const char *path, char *buf, size_t size, off_t offset,
403 struct fuse_file_info *fi)
405 int blocksize = swap_be32(omfs_info.super->blocksize);
406 u64 requested = offset / blocksize;
407 int start = offset % blocksize;
408 int count;
409 int copied = 0;
411 for (; copied < size ; copied += count, requested++)
413 count = min(size-copied, blocksize-start);
414 u8 *block = omfs_get_data_n(requested, fi);
415 if (!block)
416 goto out;
418 memcpy(&buf[copied], block + start, count);
419 free(block);
420 start = 0;
422 out:
423 return copied;
426 static int omfs_utimens(const char *path, const struct timespec tv[2])
428 omfs_inode_t *inode = omfs_lookup(path);
430 if (!inode)
431 return -ENOENT;
433 u64 ctime = tv[1].tv_sec * 1000LL + tv[1].tv_nsec/1000;
435 inode->ctime = swap_be64(ctime);
436 cache_save_inode(inode);
437 return 0;
440 // purge any empty rows and rewrite terminator
441 static void update_extent_table(struct omfs_extent *oe)
443 struct omfs_extent_entry *entry;
444 int count = 0, total_extents = 1, remaining;
446 entry = &oe->entry;
447 remaining = swap_be32(oe->extent_count);
449 for (; remaining > 1; remaining--)
451 u64 num_blocks = swap_be64(entry->blocks);
452 count += num_blocks;
453 if (num_blocks == 0)
454 memcpy(entry, entry + 1,
455 sizeof(struct omfs_extent_entry) * remaining);
456 else {
457 total_extents++;
458 entry++;
461 // entry points at terminator
462 entry->cluster = ~0;
463 entry->blocks = swap_be64(~count);
464 oe->extent_count = swap_be32(total_extents);
467 static int shrink_file(struct omfs_inode *inode, u64 size)
469 struct omfs_extent *oe;
470 struct omfs_extent_entry *entry, *term;
471 int blocksize = swap_be32(omfs_info.super->blocksize);
472 u64 requested = (size + blocksize-1)/ blocksize;
473 u64 block;
475 if (swap_be64(inode->size) == size)
476 goto out;
478 block = omfs_find_location(requested, inode, &oe, &entry);
480 inode->size = swap_be64(size);
482 // already truncated...
483 if (!block)
485 // FIXME fsx hits this case
486 cache_save_inode(inode);
487 goto out;
490 // entry points to the last valid extent, with num_blocks-(block-cluster)
491 // blocks to free. Then we free everything else and rebuild the current
492 // terminator.
494 u64 to_delete = swap_be64(entry->blocks);
495 entry->blocks = swap_be64(block - swap_be64(entry->cluster));
496 to_delete -= swap_be64(entry->blocks);
497 omfs_clear_range(&omfs_info, block, to_delete);
498 entry++;
500 for (;;)
502 u64 next = swap_be64(oe->next);
503 term = &oe->entry + swap_be32(oe->extent_count) - 1;
504 while (entry != term) {
505 omfs_clear_range(&omfs_info, swap_be64(entry->cluster),
506 swap_be64(entry->blocks));
507 entry->blocks = 0;
508 entry++;
511 // FIXME clear inode allocation bits here if needed...
512 update_extent_table(oe);
513 cache_save_inode(inode);
515 if (next == ~0)
516 break;
518 cache_put_inode(inode);
519 inode = cache_get_inode(next);
520 if (!inode)
521 goto out;
522 oe = (struct omfs_extent *) ((u8*) inode + OMFS_EXTENT_CONT);
524 out:
525 if (inode)
526 cache_put_inode(inode);
527 return 0;
530 static int grow_extent(struct omfs_inode *inode, struct omfs_extent *oe,
531 struct omfs_extent_entry *entry, int *num_added)
533 struct omfs_extent_entry *term;
534 int ret=0, max_count, to_alloc;
535 u64 new_block;
536 term = &oe->entry + swap_be32(oe->extent_count) - 1;
538 if (entry != term)
540 // try extending current extent
541 new_block = swap_be64(entry->cluster) + swap_be64(entry->blocks);
543 if (omfs_allocate_one_block(&omfs_info, new_block))
545 *num_added = 1;
546 entry->blocks = swap_be64(swap_be64(entry->blocks) + 1);
547 term->blocks = ~(swap_be64(swap_be64(~term->blocks) + 1));
548 omfs_clear_data(&omfs_info, new_block, 1);
549 goto out;
552 max_count = swap_be32(omfs_info.super->sys_blocksize) -
553 OMFS_EXTENT_START - sizeof(struct omfs_extent) /
554 sizeof(struct omfs_extent_entry) + 1;
556 if (swap_be32(oe->extent_count) > max_count-1) {
557 // no more room, add to next ptr...
558 ret = omfs_allocate_block(&omfs_info,
559 swap_be32(omfs_info.super->mirrors), &new_block);
560 if (ret)
561 goto out_fail;
563 inode = cache_new_inode(&omfs_info, new_block, "",
564 OMFS_INODE_CONTINUATION);
565 oe = (struct omfs_extent *) ((u8*) inode + OMFS_EXTENT_CONT);
566 term = &oe->entry;
569 to_alloc = swap_be32(omfs_info.root->clustersize);
570 ret = omfs_allocate_block(&omfs_info, to_alloc, &new_block);
572 if (ret)
573 goto out_fail;
575 omfs_clear_data(&omfs_info, new_block, to_alloc);
576 oe->extent_count = swap_be32(1 + swap_be32(oe->extent_count));
578 entry = term;
579 term++;
580 memcpy(term, entry, sizeof(struct omfs_extent_entry));
582 *num_added = to_alloc;
583 entry->cluster = swap_be64(new_block);
584 entry->blocks = swap_be64(*num_added);
586 term->blocks = ~(swap_be64(swap_be64(~term->blocks) + *num_added));
588 cache_save_inode(inode);
589 out:
590 out_fail:
591 return ret;
594 static int grow_file(struct omfs_inode *inode, u64 size)
596 struct omfs_extent *oe;
597 struct omfs_extent_entry *entry;
598 int ret, i;
599 int num_added;
601 int blocksize = swap_be32(omfs_info.super->blocksize);
602 u64 cur_blks = (swap_be64(inode->size) + blocksize-1) / blocksize;
603 u64 requested = (size + blocksize-1) / blocksize;
605 assert (size > swap_be64(inode->size));
607 omfs_find_location(requested, inode, &oe, &entry);
609 // entry points to terminator after last valid extent
610 if (entry != &oe->entry)
611 entry--;
613 for (i=0; i < requested - cur_blks; i += num_added)
615 ret = grow_extent(inode, oe, entry, &num_added);
616 if (ret)
617 return ret;
620 inode->size = swap_be64(size);
621 cache_save_inode(inode);
623 return 0;
626 static int _truncate(omfs_inode_t *inode, off_t new_size)
628 u64 old_size = swap_be64(inode->size);
630 if (new_size <= old_size)
631 return shrink_file(inode, new_size);
633 return grow_file(inode, new_size);
636 static int omfs_truncate(const char *path, off_t new_size)
638 omfs_inode_t *inode = omfs_lookup(path);
640 if (!inode)
641 return -ENOENT;
643 return _truncate(inode, new_size);
646 static int omfs_ftruncate(const char *path, off_t new_size,
647 struct fuse_file_info *fi)
649 omfs_inode_t *inode = get_handle(fi);
650 if (!inode)
651 return -ENOENT;
653 return _truncate(inode, new_size);
657 * Unlink an inode from its parent (does not shrink or delete the node).
659 static int _unlink(omfs_inode_t *inode)
661 int ret = 0;
662 omfs_inode_t *last, *next;
663 u64 *chain_ptr;
665 next = cache_get_inode(swap_be64(inode->parent));
666 if (!next)
667 return -ENOENT;
669 chain_ptr = (u64*) ((u8*) next + OMFS_DIR_START);
670 chain_ptr += omfs_compute_hash(&omfs_info, inode->name);
672 last = next;
673 while (*chain_ptr != ~0)
675 next = cache_get_inode(swap_be64(*chain_ptr));
676 if (!next) {
677 ret = -ENOENT;
678 goto out;
680 if (strcmp(next->name,inode->name) == 0)
682 *chain_ptr = next->sibling;
683 cache_save_inode(last);
684 cache_put_inode(last);
685 break;
687 chain_ptr = &next->sibling;
688 cache_put_inode(last);
689 last = next;
691 cache_put_inode(next);
692 out:
693 return ret;
696 static int omfs_unlink (const char *path)
698 u64 to_clear;
699 omfs_inode_t *inode = omfs_lookup(path);
700 int ret = 0;
702 if (!inode)
703 return -ENOENT;
705 ret = _unlink(inode);
706 if (ret)
707 goto out;
709 to_clear = swap_be64(inode->head.self);
710 shrink_file(inode, 0);
712 omfs_clear_range(&omfs_info, to_clear,
713 swap_be32(omfs_info.super->mirrors));
714 out:
715 return ret;
718 static int omfs_rename(const char *old, const char *new)
720 omfs_inode_t *inode = omfs_lookup(old);
721 omfs_inode_t *parent;
722 char *tmp, *dir, *file;
723 char *dest, *dest_dir, *dest_file;
724 u64 *table, sibling;
725 int ret = 0;
727 if (!inode)
728 return -ENOENT;
730 tmp = split_path(old, &dir, &file);
731 dest = split_path(new, &dest_dir, &dest_file);
733 // changing the name will change the hash location, so just
734 // unlink and relink in any case
735 ret = _unlink(inode);
736 if (ret)
737 goto out;
739 parent = omfs_lookup(dest_dir);
740 if (!parent)
742 ret = -ENOENT;
743 goto out;
746 strncpy(inode->name, dest_file, OMFS_NAMELEN);
747 inode->name[OMFS_NAMELEN-1] = 0;
749 table = (u64*) ((u8*) parent + OMFS_DIR_START);
750 table += omfs_compute_hash(&omfs_info, inode->name);
751 sibling = *table;
752 *table = inode->head.self;
753 inode->parent = parent->head.self;
755 cache_save_inode(parent);
756 cache_put_inode(parent);
757 out:
758 cache_save_inode(inode);
759 cache_put_inode(inode);
760 free(tmp);
761 free(dest);
762 return ret;
765 static int omfs_statfs(const char *path, struct statvfs *buf)
767 buf->f_fsid = OMFS_MAGIC;
768 buf->f_bsize = swap_be32(omfs_info.super->blocksize);
769 buf->f_frsize = buf->f_bsize;
770 buf->f_blocks = swap_be64(omfs_info.super->num_blocks);
771 buf->f_files = buf->f_blocks;
772 buf->f_namemax = OMFS_NAMELEN;
774 buf->f_bfree = buf->f_bavail = buf->f_ffree =
775 omfs_count_free(&omfs_info);
777 return 0;
780 static int omfs_write(const char *path, const char *buf, size_t size,
781 off_t offset, struct fuse_file_info *fi)
783 struct omfs_extent *oe;
784 struct omfs_extent_entry *entry;
785 int blocksize = swap_be32(omfs_info.super->blocksize);
786 u64 requested = offset / blocksize;
787 int start = offset % blocksize;
788 int count;
789 int copied = 0;
790 u8 *data;
792 omfs_inode_t *inode = get_handle(fi);
793 if (!inode)
794 return -ENOENT;
796 for (; copied < size ; copied += count, requested++)
798 count = min(size-copied, blocksize-start);
800 u64 block = omfs_find_location(requested, inode, &oe, &entry);
801 if (!block) {
802 u64 new_size = size + offset;
803 if (new_size < swap_be64(inode->size))
804 goto out;
806 if (grow_file(inode, new_size))
807 goto out;
809 block = omfs_find_location(requested, inode, &oe, &entry);
810 if (!block)
811 goto out;
814 data = omfs_get_block(&omfs_info, block);
815 if (!data)
816 goto out;
818 memcpy(data + start, buf + copied, count);
819 omfs_write_block(&omfs_info, block, data);
820 free(data);
821 start = 0;
823 out:
824 if (copied + offset > swap_be64(inode->size))
826 inode->size = swap_be64(copied + offset);
827 cache_save_inode(inode);
829 return copied;
832 static struct fuse_operations omfs_op = {
833 .getattr = omfs_getattr,
834 .readdir = omfs_readdir,
835 .open = omfs_open,
836 .read = omfs_read,
837 .write = omfs_write,
838 .rename = omfs_rename,
839 .mknod = omfs_mknod,
840 .mkdir = omfs_mkdir,
841 .utimens = omfs_utimens,
842 .statfs = omfs_statfs,
843 .truncate = omfs_truncate,
844 .ftruncate = omfs_ftruncate,
845 .unlink = omfs_unlink,
848 int main(int argc, char *argv[])
850 char *device = NULL;
851 int i, fuse_argc=0;
852 struct omfs_super_block super;
853 struct omfs_root_block root;
855 char **fuse_argv = malloc(argc * sizeof(char *));
857 for (i=0; i < argc; i++)
859 if ((strcmp(argv[i], "-a") == 0) && i + 1 < argc)
861 i++;
862 device = argv[i];
864 else
865 fuse_argv[fuse_argc++] = argv[i];
868 fuse_argv[fuse_argc] = NULL;
870 if (!device)
872 fprintf(stderr, "Usage: %s -a <device_file> <mount_point>\n", argv[0]);
873 return 1;
876 FILE *fp = fopen(device, "rb+");
877 if (!fp)
879 perror("fuse_omfs");
880 return 2;
883 omfs_info.dev = fp;
884 omfs_info.super = &super;
885 omfs_info.root = &root;
886 pthread_mutex_init(&omfs_info.dev_mutex, NULL);
888 if (omfs_read_super(&omfs_info))
890 printf ("Could not read super block\n");
891 return 3;
894 if (omfs_read_root_block(&omfs_info))
896 printf ("Could not read root block\n");
897 return 4;
900 if (omfs_load_bitmap(&omfs_info))
902 printf ("Could not load bitmap\n");
903 return 5;
906 inode_cache = g_hash_table_new(inode_cache_hash, inode_cache_compare);
908 return fuse_main(fuse_argc, fuse_argv, &omfs_op, NULL);