3 * (c) 2007 Bob Copeland
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))
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
);
64 g_hash_table_remove(inode_cache
, &inode
->head
.self
);
65 omfs_release_inode(inode
);
69 pthread_mutex_unlock(&cache_mutex
);
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
));
83 g_hash_table_replace(inode_cache
, &inode
->head
.self
, 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
);
98 inode
= omfs_get_inode(&omfs_info
, ino
);
102 ref
= cache_add_new_entry(inode
);
109 pthread_mutex_unlock(&cache_mutex
);
113 static omfs_inode_t
*cache_new_inode(omfs_info_t
*info
, u64 block
, char *name
,
116 omfs_inode_t
*new_inode
= omfs_new_inode(info
, block
, name
, type
);
120 pthread_mutex_lock(&cache_mutex
);
121 cache_add_new_entry(new_inode
);
122 pthread_mutex_unlock(&cache_mutex
);
127 static omfs_inode_t
*omfs_find_by_name(omfs_inode_t
*parent
, char *name
)
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
);
137 inode
= cache_get_inode(next
);
141 if (strcmp(inode
->name
,name
) == 0)
144 next
= swap_be64(inode
->sibling
);
145 cache_put_inode(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
;
167 file
= strrchr(p
, '/');
173 dir
= file
+ strlen(file
);
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
193 inode
= cache_get_inode(swap_be64(omfs_info
.root
->root_dir
));
195 tmp
= strtok_r(p
, "/", &save_ptr
);
198 tmp_inode
= omfs_find_by_name(inode
, tmp
);
199 cache_put_inode(inode
);
201 tmp
= strtok_r(NULL
, "/", &save_ptr
);
207 static int omfs_getattr(const char *path
, struct stat
*stbuf
)
210 struct fuse_context
*ctx
;
212 omfs_inode_t
*inode
= omfs_lookup(path
);
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;
222 stbuf
->st_mode
= S_IFREG
| 0644;
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
;
235 static int omfs_readdir(const char *path
, void *buf
, fuse_fill_dir_t filler
,
236 off_t offset
, struct fuse_file_info
*fi
)
240 omfs_inode_t
*dir
, *tmp
;
242 dir
= omfs_lookup(path
);
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
);
258 tmp
= cache_get_inode(inum
);
262 filler(buf
, tmp
->name
, NULL
, 0);
263 inum
= swap_be64(tmp
->sibling
);
265 cache_put_inode(tmp
);
271 static int _add_inode(const char *path
, char type
)
273 char *dir
, *file
, *tmp
;
275 omfs_inode_t
*parent
, *new_inode
;
279 tmp
= split_path(path
, &dir
, &file
);
283 parent
= omfs_lookup(dir
);
289 err
= omfs_allocate_block(&omfs_info
, swap_be32(omfs_info
.super
->mirrors
),
294 new_inode
= cache_new_inode(&omfs_info
, block
, file
, type
);
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
);
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
);
333 set_handle(fi
, inode
);
337 static u64
find_block(struct omfs_extent_entry
**entry
, u64 block
, int count
)
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
;
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
;
363 oe
= (struct omfs_extent
*) ((u8
*) inode
+ OMFS_EXTENT_START
);
367 int extent_count
= swap_be32(oe
->extent_count
);
368 u64 next
= swap_be64(oe
->next
);
371 found_block
= find_block(&entry
, requested
, extent_count
);
372 if (found_block
!= 0)
378 inode
= cache_get_inode(next
);
379 oe
= (struct omfs_extent
*) ((u8
*) inode
+ OMFS_EXTENT_CONT
);
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
);
397 return omfs_get_block(&omfs_info
, block
);
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
;
411 for (; copied
< size
; copied
+= count
, requested
++)
413 count
= min(size
-copied
, blocksize
-start
);
414 u8
*block
= omfs_get_data_n(requested
, fi
);
418 memcpy(&buf
[copied
], block
+ start
, count
);
426 static int omfs_utimens(const char *path
, const struct timespec tv
[2])
428 omfs_inode_t
*inode
= omfs_lookup(path
);
433 u64 ctime
= tv
[1].tv_sec
* 1000LL + tv
[1].tv_nsec
/1000;
435 inode
->ctime
= swap_be64(ctime
);
436 cache_save_inode(inode
);
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
;
447 remaining
= swap_be32(oe
->extent_count
);
449 for (; remaining
> 1; remaining
--)
451 u64 num_blocks
= swap_be64(entry
->blocks
);
454 memcpy(entry
, entry
+ 1,
455 sizeof(struct omfs_extent_entry
) * remaining
);
461 // entry points at terminator
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
;
475 if (swap_be64(inode
->size
) == size
)
478 block
= omfs_find_location(requested
, inode
, &oe
, &entry
);
480 inode
->size
= swap_be64(size
);
482 // already truncated...
485 // FIXME fsx hits this case
486 cache_save_inode(inode
);
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
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
);
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
));
511 // FIXME clear inode allocation bits here if needed...
512 update_extent_table(oe
);
513 cache_save_inode(inode
);
518 cache_put_inode(inode
);
519 inode
= cache_get_inode(next
);
522 oe
= (struct omfs_extent
*) ((u8
*) inode
+ OMFS_EXTENT_CONT
);
526 cache_put_inode(inode
);
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
;
536 term
= &oe
->entry
+ swap_be32(oe
->extent_count
) - 1;
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
))
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);
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
);
563 inode
= cache_new_inode(&omfs_info
, new_block
, "",
564 OMFS_INODE_CONTINUATION
);
565 oe
= (struct omfs_extent
*) ((u8
*) inode
+ OMFS_EXTENT_CONT
);
569 to_alloc
= swap_be32(omfs_info
.root
->clustersize
);
570 ret
= omfs_allocate_block(&omfs_info
, to_alloc
, &new_block
);
575 omfs_clear_data(&omfs_info
, new_block
, to_alloc
);
576 oe
->extent_count
= swap_be32(1 + swap_be32(oe
->extent_count
));
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
);
594 static int grow_file(struct omfs_inode
*inode
, u64 size
)
596 struct omfs_extent
*oe
;
597 struct omfs_extent_entry
*entry
;
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
)
613 for (i
=0; i
< requested
- cur_blks
; i
+= num_added
)
615 ret
= grow_extent(inode
, oe
, entry
, &num_added
);
620 inode
->size
= swap_be64(size
);
621 cache_save_inode(inode
);
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
);
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
);
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
)
662 omfs_inode_t
*last
, *next
;
665 next
= cache_get_inode(swap_be64(inode
->parent
));
669 chain_ptr
= (u64
*) ((u8
*) next
+ OMFS_DIR_START
);
670 chain_ptr
+= omfs_compute_hash(&omfs_info
, inode
->name
);
673 while (*chain_ptr
!= ~0)
675 next
= cache_get_inode(swap_be64(*chain_ptr
));
680 if (strcmp(next
->name
,inode
->name
) == 0)
682 *chain_ptr
= next
->sibling
;
683 cache_save_inode(last
);
684 cache_put_inode(last
);
687 chain_ptr
= &next
->sibling
;
688 cache_put_inode(last
);
691 cache_put_inode(next
);
696 static int omfs_unlink (const char *path
)
699 omfs_inode_t
*inode
= omfs_lookup(path
);
705 ret
= _unlink(inode
);
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
));
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
;
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
);
739 parent
= omfs_lookup(dest_dir
);
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
);
752 *table
= inode
->head
.self
;
753 inode
->parent
= parent
->head
.self
;
755 cache_save_inode(parent
);
756 cache_put_inode(parent
);
758 cache_save_inode(inode
);
759 cache_put_inode(inode
);
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
);
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
;
792 omfs_inode_t
*inode
= get_handle(fi
);
796 for (; copied
< size
; copied
+= count
, requested
++)
798 count
= min(size
-copied
, blocksize
-start
);
800 u64 block
= omfs_find_location(requested
, inode
, &oe
, &entry
);
802 u64 new_size
= size
+ offset
;
803 if (new_size
< swap_be64(inode
->size
))
806 if (grow_file(inode
, new_size
))
809 block
= omfs_find_location(requested
, inode
, &oe
, &entry
);
814 data
= omfs_get_block(&omfs_info
, block
);
818 memcpy(data
+ start
, buf
+ copied
, count
);
819 omfs_write_block(&omfs_info
, block
, data
);
824 if (copied
+ offset
> swap_be64(inode
->size
))
826 inode
->size
= swap_be64(copied
+ offset
);
827 cache_save_inode(inode
);
832 static struct fuse_operations omfs_op
= {
833 .getattr
= omfs_getattr
,
834 .readdir
= omfs_readdir
,
838 .rename
= omfs_rename
,
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
[])
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
)
865 fuse_argv
[fuse_argc
++] = argv
[i
];
868 fuse_argv
[fuse_argc
] = NULL
;
872 fprintf(stderr
, "Usage: %s -a <device_file> <mount_point>\n", argv
[0]);
876 FILE *fp
= fopen(device
, "rb+");
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");
894 if (omfs_read_root_block(&omfs_info
))
896 printf ("Could not read root block\n");
900 if (omfs_load_bitmap(&omfs_info
))
902 printf ("Could not load bitmap\n");
906 inode_cache
= g_hash_table_new(inode_cache_hash
, inode_cache_compare
);
908 return fuse_main(fuse_argc
, fuse_argv
, &omfs_op
, NULL
);