|
|
- #define _GNU_SOURCE
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <stdint.h>
- #include <limits.h>
- #include <dirent.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <errno.h>
- #include <assert.h>
-
- typedef int bool;
-
- #define __error(msg, quit, ...) \
- do { \
- fprintf(stderr, #msg ": function %s - line %d: ", __FUNCTION__, __LINE__); \
- if (errno != 0) { \
- fprintf(stderr, "[error] %s: ", strerror(errno)); \
- } \
- fprintf(stderr, "\n\t"), fprintf(stderr, __VA_ARGS__); \
- errno = 0; \
- if (quit) { \
- exit(-1); \
- } \
- } while (0)
-
- #define warn(...) __error(warn, 0, __VA_ARGS__)
- #define bug(...) __error(bug, 1, __VA_ARGS__)
-
- #define static_assert(x) \
- switch (x) {case 0: case (x): ; }
-
- /* 2^31 + 2^29 - 2^25 + 2^22 - 2^19 - 2^16 + 1 */
- #define GOLDEN_RATIO_PRIME_32 0x9e370001UL
-
- #define HASH_SHIFT 10
- #define HASH_LIST_SIZE (1 << HASH_SHIFT)
-
- static inline uint32_t
- __hash32(uint32_t val, unsigned int bits) {
- uint32_t hash = val * GOLDEN_RATIO_PRIME_32;
- return (hash >> (32 - bits));
- }
-
- static uint32_t
- hash32(uint32_t val) {
- return __hash32(val, HASH_SHIFT);
- }
-
- static uint32_t
- hash64(uint64_t val) {
- return __hash32((uint32_t)val, HASH_SHIFT);
- }
-
- void *
- safe_malloc(size_t size) {
- void *ret;
- if ((ret = malloc(size)) == NULL) {
- bug("malloc %lu bytes failed.\n", (long unsigned)size);
- }
- return ret;
- }
-
- char *
- safe_strdup(const char *str) {
- char *ret;
- if ((ret = strdup(str)) == NULL) {
- bug("strdup failed: %s\n", str);
- }
- return ret;
- }
-
- struct stat *
- safe_stat(const char *filename) {
- static struct stat __stat;
- if (stat(filename, &__stat) != 0) {
- bug("stat %s failed.\n", filename);
- }
- return &__stat;
- }
-
- struct stat *
- safe_fstat(int fd) {
- static struct stat __stat;
- if (fstat(fd, &__stat) != 0) {
- bug("fstat %d failed.\n", fd);
- }
- return &__stat;
- }
-
- struct stat *
- safe_lstat(const char *name) {
- static struct stat __stat;
- if (lstat(name, &__stat) != 0) {
- bug("lstat '%s' failed.\n", name);
- }
- return &__stat;
- }
-
- void
- safe_fchdir(int fd) {
- if (fchdir(fd) != 0) {
- bug("fchdir failed %d.\n", fd);
- }
- }
-
- #define SFS_MAGIC 0x2f8dbe2a
- #define SFS_NDIRECT 12
- #define SFS_BLKSIZE 4096 // 4K
- #define SFS_MAX_NBLKS (1024UL * 512) // 4K * 512K
- #define SFS_MAX_INFO_LEN 31
- #define SFS_MAX_FNAME_LEN 255
- #define SFS_MAX_FILE_SIZE (1024UL * 1024 * 128) // 128M
-
- #define SFS_BLKBITS (SFS_BLKSIZE * CHAR_BIT)
- #define SFS_TYPE_FILE 1
- #define SFS_TYPE_DIR 2
- #define SFS_TYPE_LINK 3
-
- #define SFS_BLKN_SUPER 0
- #define SFS_BLKN_ROOT 1
- #define SFS_BLKN_FREEMAP 2
-
- struct cache_block {
- uint32_t ino;
- struct cache_block *hash_next;
- void *cache;
- };
-
- struct cache_inode {
- struct inode {
- uint32_t size;
- uint16_t type;
- uint16_t nlinks;
- uint32_t blocks;
- uint32_t direct[SFS_NDIRECT];
- uint32_t indirect;
- uint32_t db_indirect;
- } inode;
- ino_t real;
- uint32_t ino;
- uint32_t nblks;
- struct cache_block *l1, *l2;
- struct cache_inode *hash_next;
- };
-
- struct sfs_fs {
- struct {
- uint32_t magic;
- uint32_t blocks;
- uint32_t unused_blocks;
- char info[SFS_MAX_INFO_LEN + 1];
- } super;
- struct subpath {
- struct subpath *next, *prev;
- char *subname;
- } __sp_nil, *sp_root, *sp_end;
- int imgfd;
- uint32_t ninos, next_ino;
- struct cache_inode *root;
- struct cache_inode *inodes[HASH_LIST_SIZE];
- struct cache_block *blocks[HASH_LIST_SIZE];
- };
-
- struct sfs_entry {
- uint32_t ino;
- char name[SFS_MAX_FNAME_LEN + 1];
- };
-
- static uint32_t
- sfs_alloc_ino(struct sfs_fs *sfs) {
- if (sfs->next_ino < sfs->ninos) {
- sfs->super.unused_blocks --;
- return sfs->next_ino ++;
- }
- bug("out of disk space.\n");
- }
-
- static struct cache_block *
- alloc_cache_block(struct sfs_fs *sfs, uint32_t ino) {
- struct cache_block *cb = safe_malloc(sizeof(struct cache_block));
- cb->ino = (ino != 0) ? ino : sfs_alloc_ino(sfs);
- cb->cache = memset(safe_malloc(SFS_BLKSIZE), 0, SFS_BLKSIZE);
- struct cache_block **head = sfs->blocks + hash32(ino);
- cb->hash_next = *head, *head = cb;
- return cb;
- }
-
- struct cache_block *
- search_cache_block(struct sfs_fs *sfs, uint32_t ino) {
- struct cache_block *cb = sfs->blocks[hash32(ino)];
- while (cb != NULL && cb->ino != ino) {
- cb = cb->hash_next;
- }
- return cb;
- }
-
- static struct cache_inode *
- alloc_cache_inode(struct sfs_fs *sfs, ino_t real, uint32_t ino, uint16_t type) {
- struct cache_inode *ci = safe_malloc(sizeof(struct cache_inode));
- ci->ino = (ino != 0) ? ino : sfs_alloc_ino(sfs);
- ci->real = real, ci->nblks = 0, ci->l1 = ci->l2 = NULL;
- struct inode *inode = &(ci->inode);
- memset(inode, 0, sizeof(struct inode));
- inode->type = type;
- struct cache_inode **head = sfs->inodes + hash64(real);
- ci->hash_next = *head, *head = ci;
- return ci;
- }
-
- struct cache_inode *
- search_cache_inode(struct sfs_fs *sfs, ino_t real) {
- struct cache_inode *ci = sfs->inodes[hash64(real)];
- while (ci != NULL && ci->real != real) {
- ci = ci->hash_next;
- }
- return ci;
- }
-
- struct sfs_fs *
- create_sfs(int imgfd) {
- uint32_t ninos, next_ino;
- struct stat *stat = safe_fstat(imgfd);
- if ((ninos = stat->st_size / SFS_BLKSIZE) > SFS_MAX_NBLKS) {
- ninos = SFS_MAX_NBLKS;
- warn("img file is too big (%llu bytes, only use %u blocks).\n",
- (unsigned long long)stat->st_size, ninos);
- }
- if ((next_ino = SFS_BLKN_FREEMAP + (ninos + SFS_BLKBITS - 1) / SFS_BLKBITS) >= ninos) {
- bug("img file is too small (%llu bytes, %u blocks, bitmap use at least %u blocks).\n",
- (unsigned long long)stat->st_size, ninos, next_ino - 2);
- }
-
- struct sfs_fs *sfs = safe_malloc(sizeof(struct sfs_fs));
- sfs->super.magic = SFS_MAGIC;
- sfs->super.blocks = ninos, sfs->super.unused_blocks = ninos - next_ino;
- snprintf(sfs->super.info, SFS_MAX_INFO_LEN, "simple file system");
-
- sfs->ninos = ninos, sfs->next_ino = next_ino, sfs->imgfd = imgfd;
- sfs->sp_root = sfs->sp_end = &(sfs->__sp_nil);
- sfs->sp_end->prev = sfs->sp_end->next = NULL;
-
- int i;
- for (i = 0; i < HASH_LIST_SIZE; i ++) {
- sfs->inodes[i] = NULL;
- sfs->blocks[i] = NULL;
- }
-
- sfs->root = alloc_cache_inode(sfs, 0, SFS_BLKN_ROOT, SFS_TYPE_DIR);
- return sfs;
- }
-
- static void
- subpath_push(struct sfs_fs *sfs, const char *subname) {
- struct subpath *subpath = safe_malloc(sizeof(struct subpath));
- subpath->subname = safe_strdup(subname);
- sfs->sp_end->next = subpath;
- subpath->prev = sfs->sp_end;
- subpath->next = NULL;
- sfs->sp_end = subpath;
- }
-
- static void
- subpath_pop(struct sfs_fs *sfs) {
- assert(sfs->sp_root != sfs->sp_end);
- struct subpath *subpath = sfs->sp_end;
- sfs->sp_end = sfs->sp_end->prev, sfs->sp_end->next = NULL;
- free(subpath->subname), free(subpath);
- }
-
- static void
- subpath_show(FILE *fout, struct sfs_fs *sfs, const char *name) {
- struct subpath *subpath = sfs->sp_root;
- fprintf(fout, "current is: /");
- while ((subpath = subpath->next) != NULL) {
- fprintf(fout, "%s/", subpath->subname);
- }
- if (name != NULL) {
- fprintf(fout, "%s", name);
- }
- fprintf(fout, "\n");
- }
-
- static void
- write_block(struct sfs_fs *sfs, void *data, size_t len, uint32_t ino) {
- assert(len <= SFS_BLKSIZE && ino < sfs->ninos);
- static char buffer[SFS_BLKSIZE];
- if (len != SFS_BLKSIZE) {
- memset(buffer, 0, sizeof(buffer));
- data = memcpy(buffer, data, len);
- }
- off_t offset = (off_t)ino * SFS_BLKSIZE;
- ssize_t ret;
- if ((ret = pwrite(sfs->imgfd, data, SFS_BLKSIZE, offset)) != SFS_BLKSIZE) {
- bug("write %u block failed: (%d/%d).\n", ino, (int)ret, SFS_BLKSIZE);
- }
- }
-
- static void
- flush_cache_block(struct sfs_fs *sfs, struct cache_block *cb) {
- write_block(sfs, cb->cache, SFS_BLKSIZE, cb->ino);
- }
-
- static void
- flush_cache_inode(struct sfs_fs *sfs, struct cache_inode *ci) {
- write_block(sfs, &(ci->inode), sizeof(ci->inode), ci->ino);
- }
-
- void
- close_sfs(struct sfs_fs *sfs) {
- static char buffer[SFS_BLKSIZE];
- uint32_t i, j, ino = SFS_BLKN_FREEMAP;
- uint32_t ninos = sfs->ninos, next_ino = sfs->next_ino;
- for (i = 0; i < ninos; ino ++, i += SFS_BLKBITS) {
- memset(buffer, 0, sizeof(buffer));
- if (i + SFS_BLKBITS > next_ino) {
- uint32_t start = 0, end = SFS_BLKBITS;
- if (i < next_ino) {
- start = next_ino - i;
- }
- if (i + SFS_BLKBITS > ninos) {
- end = ninos - i;
- }
- uint32_t *data = (uint32_t *)buffer;
- const uint32_t bits = sizeof(bits) * CHAR_BIT;
- for (j = start; j < end; j ++) {
- data[j / bits] |= (1 << (j % bits));
- }
- }
- write_block(sfs, buffer, sizeof(buffer), ino);
- }
- write_block(sfs, &(sfs->super), sizeof(sfs->super), SFS_BLKN_SUPER);
-
- for (i = 0; i < HASH_LIST_SIZE; i ++) {
- struct cache_block *cb = sfs->blocks[i];
- while (cb != NULL) {
- flush_cache_block(sfs, cb);
- cb = cb->hash_next;
- }
- struct cache_inode *ci = sfs->inodes[i];
- while (ci != NULL) {
- flush_cache_inode(sfs, ci);
- ci = ci->hash_next;
- }
- }
- }
-
- struct sfs_fs *
- open_img(const char *imgname) {
- const char *expect = ".img", *ext = imgname + strlen(imgname) - strlen(expect);
- if (ext <= imgname || strcmp(ext, expect) != 0) {
- bug("invalid .img file name '%s'.\n", imgname);
- }
- int imgfd;
- if ((imgfd = open(imgname, O_WRONLY)) < 0) {
- bug("open '%s' failed.\n", imgname);
- }
- return create_sfs(imgfd);
- }
-
- #define open_bug(sfs, name, ...) \
- do { \
- subpath_show(stderr, sfs, name); \
- bug(__VA_ARGS__); \
- } while (0)
-
- #define show_fullpath(sfs, name) subpath_show(stderr, sfs, name)
-
- void open_dir(struct sfs_fs *sfs, struct cache_inode *current, struct cache_inode *parent);
- void open_file(struct sfs_fs *sfs, struct cache_inode *file, const char *filename, int fd);
- void open_link(struct sfs_fs *sfs, struct cache_inode *file, const char *filename);
-
- #define SFS_BLK_NENTRY (SFS_BLKSIZE / sizeof(uint32_t))
- #define SFS_L0_NBLKS SFS_NDIRECT
- #define SFS_L1_NBLKS (SFS_BLK_NENTRY + SFS_L0_NBLKS)
- #define SFS_L2_NBLKS (SFS_BLK_NENTRY * SFS_BLK_NENTRY + SFS_L1_NBLKS)
- #define SFS_LN_NBLKS (SFS_MAX_FILE_SIZE / SFS_BLKSIZE)
-
- static void
- update_cache(struct sfs_fs *sfs, struct cache_block **cbp, uint32_t *inop) {
- uint32_t ino = *inop;
- struct cache_block *cb = *cbp;
- if (ino == 0) {
- cb = alloc_cache_block(sfs, 0);
- ino = cb->ino;
- }
- else if (cb == NULL || cb->ino != ino) {
- cb = search_cache_block(sfs, ino);
- assert(cb != NULL && cb->ino == ino);
- }
- *cbp = cb, *inop = ino;
- }
-
- static void
- append_block(struct sfs_fs *sfs, struct cache_inode *file, size_t size, uint32_t ino, const char *filename) {
- static_assert(SFS_LN_NBLKS <= SFS_L2_NBLKS);
- assert(size <= SFS_BLKSIZE);
- uint32_t nblks = file->nblks;
- struct inode *inode = &(file->inode);
- if (nblks >= SFS_LN_NBLKS) {
- open_bug(sfs, filename, "file is too big.\n");
- }
- if (nblks < SFS_L0_NBLKS) {
- inode->direct[nblks] = ino;
- }
- else if (nblks < SFS_L1_NBLKS) {
- nblks -= SFS_L0_NBLKS;
- update_cache(sfs, &(file->l1), &(inode->indirect));
- uint32_t *data = file->l1->cache;
- data[nblks] = ino;
- }
- else if (nblks < SFS_L2_NBLKS) {
- nblks -= SFS_L1_NBLKS;
- update_cache(sfs, &(file->l2), &(inode->db_indirect));
- uint32_t *data2 = file->l2->cache;
- update_cache(sfs, &(file->l1), &data2[nblks / SFS_BLK_NENTRY]);
- uint32_t *data1 = file->l1->cache;
- data1[nblks % SFS_BLK_NENTRY] = ino;
- }
- file->nblks ++;
- inode->size += size;
- inode->blocks ++;
- }
-
- static void
- add_entry(struct sfs_fs *sfs, struct cache_inode *current, struct cache_inode *file, const char *name) {
- static struct sfs_entry __entry, *entry = &__entry;
- assert(current->inode.type == SFS_TYPE_DIR && strlen(name) <= SFS_MAX_FNAME_LEN);
- entry->ino = file->ino, strcpy(entry->name, name);
- uint32_t entry_ino = sfs_alloc_ino(sfs);
- write_block(sfs, entry, sizeof(entry->name), entry_ino);
- append_block(sfs, current, sizeof(entry->name), entry_ino, name);
- file->inode.nlinks ++;
- }
-
- static void
- add_dir(struct sfs_fs *sfs, struct cache_inode *parent, const char *dirname, int curfd, int fd, ino_t real) {
- assert(search_cache_inode(sfs, real) == NULL);
- struct cache_inode *current = alloc_cache_inode(sfs, real, 0, SFS_TYPE_DIR);
- safe_fchdir(fd), subpath_push(sfs, dirname);
- open_dir(sfs, current, parent);
- safe_fchdir(curfd), subpath_pop(sfs);
- add_entry(sfs, parent, current, dirname);
- }
-
- static void
- add_file(struct sfs_fs *sfs, struct cache_inode *current, const char *filename, int fd, ino_t real) {
- struct cache_inode *file;
- if ((file = search_cache_inode(sfs, real)) == NULL) {
- file = alloc_cache_inode(sfs, real, 0, SFS_TYPE_FILE);
- open_file(sfs, file, filename, fd);
- }
- add_entry(sfs, current, file, filename);
- }
-
- static void
- add_link(struct sfs_fs *sfs, struct cache_inode *current, const char *filename, ino_t real) {
- struct cache_inode *file = alloc_cache_inode(sfs, real, 0, SFS_TYPE_LINK);
- open_link(sfs, file, filename);
- add_entry(sfs, current, file, filename);
- }
-
- void
- open_dir(struct sfs_fs *sfs, struct cache_inode *current, struct cache_inode *parent) {
- DIR *dir;
- if ((dir = opendir(".")) == NULL) {
- open_bug(sfs, NULL, "opendir failed.\n");
- }
- add_entry(sfs, current, current, ".");
- add_entry(sfs, current, parent, "..");
- struct dirent *direntp;
- while ((direntp = readdir(dir)) != NULL) {
- const char *name = direntp->d_name;
- if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
- continue ;
- }
- if (name[0] == '.') {
- continue ;
- }
- if (strlen(name) > SFS_MAX_FNAME_LEN) {
- open_bug(sfs, NULL, "file name is too long: %s\n", name);
- }
- struct stat *stat = safe_lstat(name);
- if (S_ISLNK(stat->st_mode)) {
- add_link(sfs, current, name, stat->st_ino);
- }
- else {
- int fd;
- if ((fd = open(name, O_RDONLY)) < 0) {
- open_bug(sfs, NULL, "open failed: %s\n", name);
- }
- if (S_ISDIR(stat->st_mode)) {
- add_dir(sfs, current, name, dirfd(dir), fd, stat->st_ino);
- }
- else if (S_ISREG(stat->st_mode)) {
- add_file(sfs, current, name, fd, stat->st_ino);
- }
- else {
- char mode = '?';
- if (S_ISFIFO(stat->st_mode)) mode = 'f';
- if (S_ISSOCK(stat->st_mode)) mode = 's';
- if (S_ISCHR(stat->st_mode)) mode = 'c';
- if (S_ISBLK(stat->st_mode)) mode = 'b';
- show_fullpath(sfs, NULL);
- warn("unsupported mode %07x (%c): file %s\n", stat->st_mode, mode, name);
- }
- close(fd);
- }
- }
- closedir(dir);
- }
-
- void
- open_file(struct sfs_fs *sfs, struct cache_inode *file, const char *filename, int fd) {
- static char buffer[SFS_BLKSIZE];
- ssize_t ret, last = SFS_BLKSIZE;
- while ((ret = read(fd, buffer, sizeof(buffer))) != 0) {
- assert(last == SFS_BLKSIZE);
- uint32_t ino = sfs_alloc_ino(sfs);
- write_block(sfs, buffer, ret, ino);
- append_block(sfs, file, ret, ino, filename);
- last = ret;
- }
- if (ret < 0) {
- open_bug(sfs, filename, "read file failed.\n");
- }
- }
-
- void
- open_link(struct sfs_fs *sfs, struct cache_inode *file, const char *filename) {
- static char buffer[SFS_BLKSIZE];
- uint32_t ino = sfs_alloc_ino(sfs);
- ssize_t ret = readlink(filename, buffer, sizeof(buffer));
- if (ret < 0 || ret == SFS_BLKSIZE) {
- open_bug(sfs, filename, "read link failed, %d", (int)ret);
- }
- write_block(sfs, buffer, ret, ino);
- append_block(sfs, file, ret, ino, filename);
- }
-
- int
- create_img(struct sfs_fs *sfs, const char *home) {
- int curfd, homefd;
- if ((curfd = open(".", O_RDONLY)) < 0) {
- bug("get current fd failed.\n");
- }
- if ((homefd = open(home, O_RDONLY | O_NOFOLLOW)) < 0) {
- bug("open home directory '%s' failed.\n", home);
- }
- safe_fchdir(homefd);
- open_dir(sfs, sfs->root, sfs->root);
- safe_fchdir(curfd);
- close(curfd), close(homefd);
- close_sfs(sfs);
- return 0;
- }
-
- static void
- static_check(void) {
- static_assert(sizeof(off_t) == 8);
- static_assert(sizeof(ino_t) == 8);
- static_assert(SFS_MAX_NBLKS <= 0x80000000UL);
- static_assert(SFS_MAX_FILE_SIZE <= 0x80000000UL);
- }
-
- int
- main(int argc, char **argv) {
- static_check();
- if (argc != 3) {
- bug("usage: <input *.img> <input dirname>\n");
- }
- const char *imgname = argv[1], *home = argv[2];
- if (create_img(open_img(imgname), home) != 0) {
- bug("create img failed.\n");
- }
- printf("create %s (%s) successfully.\n", imgname, home);
- return 0;
- }
-
|