metal-cos/sbin/newfs_o2fs/newfs_o2fs.c

363 lines
7.6 KiB
C

#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <getopt.h>
#include <o2fs.h>
#define MAXBLOCKSIZE (64*1024*1024)
char tempbuf[MAXBLOCKSIZE];
char zerobuf[MAXBLOCKSIZE];
bool verbose = false;
bool hasManifest = false;
uint64_t diskSize = 0;
uint64_t diskOffset = 0;
uint64_t blockSize = 16*1024;
int diskfd;
struct stat diskstat;
#define TOKEN_EOF 0
#define TOKEN_DIR 1
#define TOKEN_END 2
#define TOKEN_FILE 3
#define TOKEN_STRING 4
char *tokenBuf;
char *tokenCur;
char tokenString[512];
void LoadManifest(const char *manifest)
{
int fd = open(manifest, O_RDONLY);
struct stat manifeststat;
if (fd < 0) {
perror("Cannot open manifest");
exit(1);
}
fstat(fd, &manifeststat);
tokenBuf = malloc(manifeststat.st_size + 1);
read(fd, tokenBuf, manifeststat.st_size);
tokenBuf[manifeststat.st_size] = '\0';
tokenCur = tokenBuf;
tokenString[0] = '\0';
}
int GetToken()
{
int i;
while (*tokenCur == ' ' || *tokenCur == '\t' ||
*tokenCur == '\n' || *tokenCur == '\r')
tokenCur++;
for (i = 0; i < 512; i++)
{
tokenString[i] = tokenCur[i];
if (tokenCur[i] == ' ' || tokenCur[i] == '\t' ||
tokenCur[i] == '\n' || tokenCur[i] == '\r' ||
tokenCur[i] == '\0')
{
tokenString[i] = '\0';
tokenCur += i;
break;
}
}
if (strcmp(tokenString, "") == 0)
return TOKEN_EOF;
if (strcmp(tokenString, "DIR") == 0)
return TOKEN_DIR;
if (strcmp(tokenString, "END") == 0)
return TOKEN_END;
if (strcmp(tokenString, "FILE") == 0)
return TOKEN_FILE;
return TOKEN_STRING;
}
uint64_t
AppendBlock(const void *buf, size_t len)
{
uint64_t offset = lseek(diskfd, 0, SEEK_CUR);
assert(offset % blockSize == 0);
assert(len <= blockSize);
write(diskfd, buf, len);
if (len != blockSize) {
write(diskfd, zerobuf, blockSize - len);
}
return offset;
}
ObjID *AddFile(const char *file)
{
int i = 0;
int fd;
ObjID *id = malloc(sizeof(ObjID));
BNode node;
memset(&node, 0, sizeof(node));
memcpy(node.magic, BNODE_MAGIC, 8);
node.versionMajor = O2FS_VERSION_MAJOR;
node.versionMinor = O2FS_VERSION_MINOR;
// Copy file
fd = open(file, O_RDONLY);
if (fd < 0) {
perror("Cannot open file");
exit(1);
}
while (1) {
int len = read(fd, tempbuf, blockSize);
if (len < 0) {
perror("File read error");
exit(1);
}
if (len == 0) {
break;
}
uint64_t offset = AppendBlock(tempbuf, len);
// Update node
node.direct[i].device = 0;
node.direct[i].offset = offset;
node.size += (uint64_t)len;
i++;
}
close(fd);
// Construct BNode
uint64_t offset = AppendBlock(&node, sizeof(node));
// Construct ObjID
id->device = 0;
id->offset = offset;
return id;
}
ObjID *AddDirectory()
{
int tok;
BDirEntry *entries = malloc(128 * sizeof(BDirEntry));
int entry = 0;
ObjID *id = malloc(sizeof(ObjID));
BNode node;
while (1)
{
memset(&entries[entry], 0, sizeof(BDirEntry));
tok = GetToken();
if (tok == TOKEN_FILE) {
tok = GetToken();
printf("FILE %s\n", tokenString);
strncpy((char *)entries[entry].name, tokenString, MAXNAMELEN);
tok = GetToken();
ObjID *fobj = AddFile(tokenString);
memcpy(&entries[entry].objId, fobj, sizeof(ObjID));
free(fobj);
} else if (tok == TOKEN_DIR) {
tok = GetToken();
printf("DIR %s\n", tokenString);
strncpy((char *)entries[entry].name, tokenString, MAXNAMELEN);
ObjID *dobj = AddDirectory();
memcpy(&entries[entry].objId, dobj, sizeof(ObjID));
free(dobj);
} else if (tok == TOKEN_END) {
printf("END\n");
break;
} else if (tok == TOKEN_EOF) {
printf("Unexpected end of file\n");
exit(1);
} else {
printf("Unknown token '%s'\n", tokenString);
exit(1);
}
memcpy(entries[entry].magic, BDIR_MAGIC, 8);
// entries[entry].size ...
entries[entry].flags = 0;
entries[entry].ctime = (uint64_t)time(NULL);
entries[entry].mtime = (uint64_t)time(NULL);
strncpy((char *)entries[entry].user, "root", MAXUSERNAMELEN);
strncpy((char *)entries[entry].group, "root", MAXUSERNAMELEN);
entry++;
}
// Write Directory
uint64_t dirOffset = AppendBlock(entries, entry * sizeof(BDirEntry));
uint64_t dirLength = entry * sizeof(BDirEntry);
int i;
uint64_t off;
memset(&node, 0, sizeof(node));
memcpy(node.magic, BNODE_MAGIC, 8);
node.versionMajor = O2FS_VERSION_MAJOR;
node.versionMinor = O2FS_VERSION_MINOR;
node.size = dirLength;
for (i = 0, off = 0; off <= dirLength; i++, off += blockSize) {
node.direct[i].device = 0;
node.direct[i].offset = dirOffset + off;
}
uint64_t offset = AppendBlock(&node, sizeof(node));
id->device = 0;
id->offset = offset;
return id;
}
void Superblock(ObjID *objid)
{
SuperBlock sb;
lseek(diskfd, 0, SEEK_SET);
memset(&sb, 0, sizeof(sb));
memcpy(sb.magic, SUPERBLOCK_MAGIC, 8);
sb.versionMajor = O2FS_VERSION_MAJOR;
sb.versionMinor = O2FS_VERSION_MINOR;
sb.blockCount = diskSize / blockSize;
sb.blockSize = blockSize;
if (objid)
memcpy(&sb.root, objid, sizeof(ObjID));
AppendBlock(&sb, sizeof(sb));
}
void usage()
{
printf("Usage: newfs_o2fs [OPTIONS] special-device\n");
printf("Options:\n");
printf(" -m, --manifest Manifest of files to copy to file system\n");
printf(" -s, --size Size in megabytes of device or disk image\n");
printf(" -v, --verbose Verbose logging\n");
printf(" -h, --help Print help message\n");
}
int main(int argc, char * const *argv)
{
int ch;
int status;
struct option longopts[] = {
{ "manifest", required_argument, NULL, 'm' },
{ "size", required_argument, NULL, 's' },
{ "verbose", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ NULL, 0, NULL, 0 }
};
while ((ch = getopt_long(argc, argv, "m:s:vh", longopts, NULL)) != -1)
{
switch (ch) {
case 'm':
hasManifest = true;
LoadManifest(optarg);
break;
case 's':
diskSize = atol(optarg) * 1024 * 1024;
break;
case 'v':
verbose = true;
break;
case 'h':
usage();
return 0;
default:
usage();
return 1;
}
}
argc -= optind;
argv += optind;
if (argc != 1) {
usage();
return 1;
}
diskfd = open(argv[0], O_RDWR | O_CREAT, 0660);
if (diskfd < 0) {
perror("Cannot open special device or disk image");
return 1;
}
status = fstat(diskfd, &diskstat);
if (status < 0) {
perror("Cannot fstat special device or disk image");
return 1;
}
if (diskstat.st_size == 0 && diskSize == 0) {
printf("Error: Must specify size for disk images\n");
usage();
return 1;
}
if (diskstat.st_size == 0) {
char buf[512];
lseek(diskfd, diskSize - 512, SEEK_SET);
memset(buf, 0, 512);
write(diskfd, buf, 512);
lseek(diskfd, 0, SEEK_SET);
}
diskOffset = blockSize;
lseek(diskfd, diskOffset, SEEK_SET);
memset(zerobuf, 0, MAXBLOCKSIZE);
ObjID *root = NULL;
if (hasManifest) {
int tok;
tok = GetToken();
if (tok != TOKEN_DIR) {
printf("Expected 'DIR' token, but found '%s'\n", tokenString);
exit(1);
}
tok = GetToken();
if (tok != TOKEN_STRING || strcmp(tokenString, "/") != 0) {
printf("Expected '/' token\n");
exit(1);
}
root = AddDirectory();
tok = GetToken();
if (tok != TOKEN_EOF) {
printf("Expected end-of-file, but found '%s'\n", tokenString);
exit(1);
}
}
Superblock(root);
free(root);
close(diskfd);
}