#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;
|
|
}
|
|
|