10#include <fuse3/fuse.h>
37 if (fstat(fd, &st) < 0)
42 uint32_t bm_blocks = (total_all > 32768 ? 2 : 1);
48 if (total_all <= 1 + 1 + bm_blocks + it_blocks)
64 memset(&bmaps, 0,
sizeof(bmaps));
71 memset(&root, 0,
sizeof(root));
72 root.
mode = S_IFDIR | 0755;
76 time_t now = time(NULL);
106 int fd = open(path, O_RDWR);
113 if (fstat(fd, &st) < 0) {
120 bool is_blkdev = S_ISBLK(st.st_mode);
124 if (ioctl(fd, BLKGETSIZE64, &size64) < 0) {
132 img_size = lseek(fd, 0, SEEK_END);
142 if (ftruncate(fd, img_size) < 0) {
150 if (lseek(fd, 0, SEEK_SET) < 0) {
179 im_blocks + bm_blocks);
199 if (strcmp(path,
"/") == 0) {
205 char *path_copy = strdup(path);
209 uint32_t current = 1;
210 char *token = strtok(path_copy,
"/");
213 int ret =
dir_lookup(fs, current, token, &next);
219 token = strtok(NULL,
"/");
248 memset(stbuf, 0,
sizeof(*stbuf));
249 stbuf->st_mode = inode.
mode;
250 stbuf->st_nlink = S_ISDIR(inode.
mode) ? 2 : 1;
251 stbuf->st_uid = inode.
uid;
252 stbuf->st_gid = inode.
gid;
253 stbuf->st_size = inode.
size;
254 stbuf->st_atime = inode.
atime;
255 stbuf->st_mtime = inode.
mtime;
256 stbuf->st_ctime = inode.
ctime;
273 fuse_fill_dir_t filler, off_t offset) {
277 return dir_list(fs, ino, buf, filler, offset);
321 if ((inode.
mode & S_IFDIR) == S_IFDIR)
325 if ((
size_t)offset >= inode.
size)
329 if ((
size_t)offset +
size > inode.
size)
332 size_t bytes_read = 0;
336 while (bytes_read <
size) {
337 uint32_t block_index =
339 uint32_t block_offset =
344 if (to_read >
size - bytes_read)
345 to_read =
size - bytes_read;
347 uint32_t disk_block = 0;
350 if (block_index < 12U) {
351 disk_block = inode.
direct[block_index];
360 disk_block = ptrs[block_index - 12U];
377 disk_block = l2[l2_idx];
381 if (disk_block == 0) {
382 memset(buf + bytes_read, 0, to_read);
387 memcpy(buf + bytes_read, block_buf + block_offset, to_read);
389 bytes_read += to_read;
408 size_t size, off_t offset) {
418 if ((inode.
mode & S_IFDIR) == S_IFDIR)
421 size_t bytes_written = 0;
425 while (bytes_written <
size) {
426 off_t cur_offset = offset + bytes_written;
427 uint32_t block_index =
429 uint32_t block_offset =
433 if (to_write >
size - bytes_written)
434 to_write =
size - bytes_written;
436 uint32_t *target = NULL;
437 uint32_t disk_block = 0;
440 if (block_index < 12U) {
441 target = &inode.
direct[block_index];
442 disk_block = *target;
461 target = &ptrs[block_index - 12U];
462 disk_block = *target;
465 if (disk_block == 0) {
472 *target = disk_block;
496 if (l1[l1_idx] == 0) {
511 if (l2[l2_idx] == 0) {
521 disk_block = l2[l2_idx];
522 target = &l2[l2_idx];
526 if (target && *target == 0) {
533 disk_block = *target;
541 memcpy(block_buf + block_offset, buf + bytes_written, to_write);
545 bytes_written += to_write;
549 if ((uint32_t)(offset + bytes_written) > inode.
size)
550 inode.
size = offset + bytes_written;
551 inode.
mtime = time(NULL);
556 return bytes_written;
571 struct fuse_file_info *fi) {
581 char *path_copy = strdup(path);
584 char *name = strrchr(path_copy,
'/');
585 if (!name || *(name + 1) ==
'\0') {
590 char *parent_path = (*path_copy ==
'\0') ?
"/" : path_copy;
603 if ((parent_inode.
mode & S_IFDIR) == 0) {
617 memset(&new_inode, 0,
sizeof(new_inode));
618 new_inode.
mode = (
mode & 0xFFF) | S_IFREG;
619 new_inode.
uid = getuid();
620 new_inode.
gid = getgid();
622 time_t now = time(NULL);
623 new_inode.
atime = (uint32_t)now;
624 new_inode.
mtime = (uint32_t)now;
625 new_inode.
ctime = (uint32_t)now;
630 for (
int i = 0; i < 12 && !added; ++i) {
631 if (parent_inode.
direct[i] == 0) {
634 if (new_dir_block < 0) {
644 entries[0].
inode = new_ino;
660 if (entries[j].
inode == 0) {
661 entries[j].
inode = new_ino;
683 parent_inode.
mtime = (uint32_t)now;
684 parent_inode.
ctime = (uint32_t)now;
710 char *path_copy = strdup(path);
713 char *
name = strrchr(path_copy,
'/');
719 char *parent_path = (*path_copy ==
'\0') ?
"/" : path_copy;
732 if ((parent_inode.
mode & S_IFDIR) == 0) {
746 memset(&new_inode, 0,
sizeof(new_inode));
747 new_inode.
mode = (
mode & 0xFFF) | S_IFDIR;
748 new_inode.
uid = getuid();
749 new_inode.
gid = getgid();
751 time_t now = time(NULL);
752 new_inode.
atime = (uint32_t)now;
753 new_inode.
mtime = (uint32_t)now;
754 new_inode.
ctime = (uint32_t)now;
759 for (
int i = 0; i < 12 && !added; ++i) {
760 if (parent_inode.
direct[i] == 0) {
763 if (new_dir_block < 0) {
773 entries[0].
inode = new_ino;
789 if (entries[j].
inode == 0) {
790 entries[j].
inode = new_ino;
812 parent_inode.
mtime = (uint32_t)now;
813 parent_inode.
ctime = (uint32_t)now;
838 if ((inode.
mode & S_IFDIR) == S_IFDIR) {
843 char *path_copy = strdup(path);
846 char *name = strrchr(path_copy,
'/');
848 char *parent_path = (*path_copy ==
'\0') ?
"/" : path_copy;
861 for (uint32_t i = 0; i < 12; ++i) {
862 if (parent_inode.
direct[i] == 0)
868 if (entries[j].
inode == ino &&
871 memset(&entries[j], 0,
sizeof(entries[j]));
880 for (
int i = 0; i < 12; ++i) {
881 if (
inode.direct[i] == 0)
892 if (
inode.indirect != 0) {
910 if (
inode.double_indirect != 0) {
934 inode.double_indirect = 0;
940 memset(&empty_inode, 0,
sizeof(empty_inode));
944 time_t now_time = time(NULL);
945 parent_inode.
mtime = (uint32_t)now_time;
946 parent_inode.
ctime = (uint32_t)now_time;
972 if ((inode.
mode & S_IFDIR) == 0) {
978 for (
int i = 0; i < 12; ++i) {
985 if (entries[j].
inode != 0) {
992 char *dummy_path = strdup(path);
1023 char *from_copy = strdup(from);
1024 char *to_copy = strdup(to);
1025 if (!from_copy || !to_copy) {
1030 char *from_name = strrchr(from_copy,
'/');
1031 char *to_name = strrchr(to_copy,
'/');
1034 char *from_parent_path = (*from_copy ==
'\0') ?
"/" : from_copy;
1035 char *to_parent_path = (*to_copy ==
'\0') ?
"/" : to_copy;
1040 uint32_t from_parent_ino, to_parent_ino;
1048 struct sfuse_inode from_parent_inode, to_parent_inode;
1052 if ((from_parent_inode.
mode & S_IFDIR) == 0 ||
1053 (to_parent_inode.
mode & S_IFDIR) == 0) {
1061 for (uint32_t i = 0; i < 12U; ++i) {
1062 if (from_parent_inode.
direct[i] == 0)
1067 if (entries[j].
inode == src_ino &&
1069 entries[j].
inode = 0;
1070 entries[j].
name[0] =
'\0';
1079 for (uint32_t i = 0; i < 12U; ++i) {
1080 if (to_parent_inode.
direct[i] == 0) {
1084 to_parent_inode.
direct[i] = new_blk;
1086 memset(entries, 0,
sizeof(buf_block));
1087 entries[0].
inode = src_ino;
1097 if (entries[k].
inode == 0) {
1098 entries[k].
inode = src_ino;
1109 time_t now_time = time(NULL);
1110 from_parent_inode.
mtime = from_parent_inode.
ctime = (uint32_t)now_time;
1111 to_parent_inode.
mtime = to_parent_inode.
ctime = (uint32_t)now_time;
1115 src_inode.
ctime = (uint32_t)now_time;
1148 if ((inode.
mode & S_IFDIR) == S_IFDIR) {
1152 off_t old_size = inode.
size;
1154 if (
size == old_size) {
1158 else if (
size < old_size) {
1159 uint32_t keep_blocks =
1165 for (uint32_t i = keep_blocks; i < old_blocks; ++i) {
1167 if (inode.
direct[i] != 0) {
1176 if (ptrs[idx] != 0) {
1190 if (level1_arr[l1_idx] != 0) {
1193 if (level2_arr[l2_idx] != 0) {
1195 level2_arr[l2_idx] = 0;
1224 int all_l1_empty = 1;
1226 if (level1[a] != 0) {
1229 int all_l2_empty = 1;
1231 if (level2[b] != 0) {
1264 time_t now_time = time(NULL);
1265 inode.
mtime = (uint32_t)now_time;
1266 inode.
ctime = (uint32_t)now_time;
1281 struct fuse_file_info *fi) {
1319 const struct timespec tv[2]) {
1328 inode.
atime = (uint32_t)tv[0].tv_sec;
1329 inode.
mtime = (uint32_t)tv[1].tv_sec;
1330 inode.
ctime = (uint32_t)time(NULL);
int alloc_inode(struct sfuse_superblock *sb, struct sfuse_inode_bitmap *imap)
슈퍼블록 기반으로 아이노드 할당
int bitmap_sync(int fd, uint32_t start_blk, const struct sfuse_bitmaps *bmaps, uint32_t count)
메모리 비트맵을 디스크에 동기화
void free_inode(struct sfuse_superblock *sb, struct sfuse_inode_bitmap *imap, uint32_t ino)
슈퍼블록 기반으로 아이노드 해제
int alloc_block(struct sfuse_superblock *sb, struct sfuse_block_bitmap *bmap)
슈퍼블록 기반으로 데이터 블록 할당
void free_block(struct sfuse_superblock *sb, struct sfuse_block_bitmap *bmap, uint32_t blk)
슈퍼블록 기반으로 데이터 블록 해제
ssize_t read_block(int fd, uint32_t blk, void *out_buf)
지정한 블록 번호의 데이터를 읽어 버퍼에 저장
ssize_t write_block(int fd, uint32_t blk, const void *buf)
버퍼의 내용을 지정한 블록 번호에 기록
int dir_list(const struct sfuse_fs *fs, uint32_t dir_ino, void *buf, fuse_fill_dir_t filler, off_t offset)
FUSE용 디렉터리 목록 채우기 콜백 호출
int dir_lookup(const struct sfuse_fs *fs, uint32_t dir_ino, const char *name, uint32_t *out_ino)
디렉터리에서 이름으로 inode 번호를 검색
#define DENTS_PER_BLOCK
한 블록당 디렉터리 엔트리 수
int fs_getattr(struct sfuse_fs *fs, const char *path, struct stat *stbuf)
파일 또는 디렉터리 속성 가져오기 (stat)
int fs_mkdir(struct sfuse_fs *fs, const char *path, mode_t mode)
새 디렉터리 생성
struct sfuse_fs * fs_initialize(const char *path, int *error_out)
SFUSE FS 초기화
int fs_read(struct sfuse_fs *fs, const char *path, char *buf, size_t size, off_t offset)
파일 읽기
void fs_teardown(struct sfuse_fs *fs)
파일 시스템 해제
static int fs_format_filesystem(int fd, struct sfuse_superblock *sb)
빈 이미지 파일을 VSFS 구조로 포맷
int fs_create(struct sfuse_fs *fs, const char *path, mode_t mode, struct fuse_file_info *fi)
새 파일 생성
int fs_truncate(struct sfuse_fs *fs, const char *path, off_t size)
파일 크기 조정
int fs_utimens(struct sfuse_fs *fs, const char *path, const struct timespec tv[2])
파일 접근/수정 시간 업데이트
int fs_rmdir(struct sfuse_fs *fs, const char *path)
비어 있는 디렉터리 삭제
int fs_resolve_path(struct sfuse_fs *fs, const char *path, uint32_t *out_ino)
경로 문자열을 inode 번호로 변환
int fs_open(struct sfuse_fs *fs, const char *path, struct fuse_file_info *fi)
파일 열기 (존재 여부만 확인)
int fs_fsync(struct sfuse_fs *fs, const char *path, int datasync, struct fuse_file_info *fi)
파일 시스템 동기화 (flush/fsync)
int fs_unlink(struct sfuse_fs *fs, const char *path)
파일 삭제
int fs_readdir(struct sfuse_fs *fs, const char *path, void *buf, fuse_fill_dir_t filler, off_t offset)
디렉터리 목록 가져오기
int fs_access(struct sfuse_fs *fs, const char *path, int mask)
파일 접근 권한 검사
int fs_write(struct sfuse_fs *fs, const char *path, const char *buf, size_t size, off_t offset)
파일 쓰기
int fs_rename(struct sfuse_fs *fs, const char *from, const char *to)
파일 또는 디렉터리 이름 변경 (이동)
int fs_flush(struct sfuse_fs *fs, const char *path, struct fuse_file_info *fi)
FUSE의 flush 호출 처리
bool g_force_format
강제 포맷 옵션 플래그
#define SFUSE_PTRS_PER_BLOCK
한 블록에 담을 수 있는 포인터 수 (32-bit)
int inode_load(int fd, const struct sfuse_superblock *sb, uint32_t ino, struct sfuse_inode *out)
디스크 이미지에서 아이노드를 읽어 구조체에 로드
int inode_sync(int fd, const struct sfuse_superblock *sb, uint32_t ino, const struct sfuse_inode *in)
아이노드 구조체 내용을 디스크 이미지에 동기화
아이노드 비트맵과 블록 비트맵을 함께 담는 구조체
struct sfuse_block_bitmap block
struct sfuse_inode_bitmap inode
struct sfuse_bitmaps * bmaps
struct sfuse_superblock sb
struct sfuse_inode_block * inode_table
uint32_t inode_table_start
uint32_t block_bitmap_start
uint32_t data_block_start
uint32_t inode_bitmap_start
#define SFUSE_BLOCK_SIZE
블록 크기 (바이트 단위)
#define SFUSE_MAX_INODES
최대 아이노드 수
int sb_sync(int fd, const struct sfuse_superblock *sb)
슈퍼블록을 디스크에 동기화
#define SFUSE_MAGIC
SFUSE 매직 넘버