#if HAVE_NBTOOL_CONFIG_H
#include "nbtool_config.h"
#endif
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: main.c,v 1.11 2022/02/27 21:05:11 zafer Exp $");
#endif
#include <sys/param.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <err.h>
#include "v7fs.h"
#include "v7fs_impl.h"
#include "v7fs_endian.h"
#include "v7fs_superblock.h"
#include "v7fs_inode.h"
#include "v7fs_datablock.h"
#include "newfs_v7fs.h"
#include "progress.h"
#define VPRINTF(lv, fmt, args...) { if (v7fs_newfs_verbose >= lv) \
printf(fmt, ##args); }
static v7fs_daddr_t
determine_ilist_size(v7fs_daddr_t volume_size, int32_t files)
{
v7fs_daddr_t ilist_size;
if (files)
ilist_size = howmany(files, V7FS_INODE_PER_BLOCK);
else
ilist_size = volume_size / 25;
if (ilist_size > (v7fs_daddr_t)V7FS_ILISTBLK_MAX)
ilist_size = V7FS_ILISTBLK_MAX;
return ilist_size;
}
static int
partition_check(struct v7fs_self *fs)
{
struct v7fs_superblock *sb = &fs->superblock;
int error;
if ((error = v7fs_superblock_load(fs))) {
if (error != EINVAL) {
warnx("Can't read superblock sector.");
}
}
sb->modified = 1;
if ((error = v7fs_superblock_writeback(fs))) {
if (errno == EROFS) {
warnx("Overwriting disk label? ");
}
warnx("Can't write superblock sector.");
}
return error;
}
static int
make_root(struct v7fs_self *fs)
{
struct v7fs_inode inode;
struct v7fs_dirent *dir;
int error;
memset(&inode, 0, sizeof(inode));
inode.inode_number = 1;
inode.mode = V7FS_IFREG;
v7fs_inode_writeback(fs, &inode);
v7fs_ino_t ino;
if ((error = v7fs_inode_allocate(fs, &ino))) {
errno = error;
warn("Can't allocate / inode");
return error;
}
memset(&inode, 0, sizeof(inode));
inode.inode_number = ino;
inode.mode = 0777 | V7FS_IFDIR;
inode.uid = 0;
inode.gid = 0;
inode.nlink = 2;
inode.atime = inode.mtime = inode.ctime = time(0);
v7fs_datablock_expand(fs, &inode, sizeof(*dir) * 2);
v7fs_daddr_t blk = inode.addr[0];
void *buf;
if (!(buf = scratch_read(fs, blk))) {
v7fs_inode_deallocate(fs, ino);
errno = error = EIO;
warn("Can't read / dirent.");
return error;
}
dir = (struct v7fs_dirent *)buf;
strcpy(dir[0].name, ".");
dir[0].inode_number = V7FS_VAL16(fs, ino);
strcpy(dir[1].name, "..");
dir[1].inode_number = V7FS_VAL16(fs, ino);
if (!fs->io.write(fs->io.cookie, buf, blk)) {
scratch_free(fs, buf);
errno = error = EIO;
warn("Can't write / dirent.");
return error;
}
scratch_free(fs, buf);
v7fs_inode_writeback(fs, &inode);
if ((error = v7fs_superblock_writeback(fs))) {
errno = error;
warnx("Can't write superblock.");
}
return error;
}
static v7fs_daddr_t
make_freeblocklist(struct v7fs_self *fs, v7fs_daddr_t listblk, uint8_t *buf)
{
uint32_t (*val32)(uint32_t) = fs->val.conv32;
uint16_t (*val16)(uint16_t) = fs->val.conv16;
struct v7fs_freeblock *fb = (struct v7fs_freeblock *)buf;
int i, j, k;
memset(buf, 0, V7FS_BSIZE);
for (i = V7FS_MAX_FREEBLOCK - 1, j = listblk + 1, k = 0; i >= 0;
i--, j++, k++) {
progress(0);
if (j == (int32_t)fs->superblock.volume_size)
{
VPRINTF(4, "\nlast freeblock #%d\n",
(*val32)(fb->freeblock[i + 1]));
memmove(fb->freeblock + 1, fb->freeblock + i + 1, k *
sizeof(v7fs_daddr_t));
fb->freeblock[0] = 0;
fb->nfreeblock = (*val16)(k + 1);
VPRINTF(4, "last freeblock contains #%d\n",
(*val16)(fb->nfreeblock));
fs->io.write(fs->io.cookie, buf, listblk);
return 0;
}
fb->freeblock[i] = (*val32)(j);
}
fb->nfreeblock = (*val16)(k);
if (!fs->io.write(fs->io.cookie, buf, listblk)) {
errno = EIO;
warn("blk=%ld", (long)listblk);
return 0;
}
return (*val32)(fb->freeblock[0]);
}
static int
make_filesystem(struct v7fs_self *fs, v7fs_daddr_t volume_size,
v7fs_daddr_t ilist_size)
{
struct v7fs_superblock *sb;
v7fs_daddr_t blk;
uint8_t buf[V7FS_BSIZE];
int error = 0;
int32_t i, j;
VPRINTF(4, "Zero clear ilist.\n");
progress(&(struct progress_arg){ .label = "zero ilist", .tick =
ilist_size / PROGRESS_BAR_GRANULE });
memset(buf, 0, sizeof buf);
for (i = V7FS_ILIST_SECTOR; i < (int32_t)ilist_size; i++) {
fs->io.write(fs->io.cookie, buf, i);
progress(0);
}
#ifndef HAVE_NBTOOL_CONFIG_H
progress_done();
#endif
VPRINTF(4, "\n");
sb = &fs->superblock;
sb->volume_size = volume_size;
sb->datablock_start_sector = ilist_size + V7FS_ILIST_SECTOR;
sb->update_time = time(NULL);
VPRINTF(4, "Setup inode cache.\n");
sb->nfreeinode = V7FS_MAX_FREEINODE;
for (i = V7FS_MAX_FREEINODE - 1, j = V7FS_ROOT_INODE; i >= 0; i--, j++)
sb->freeinode[i] = j;
sb->total_freeinode = ilist_size * V7FS_INODE_PER_BLOCK - 1;
VPRINTF(4, "Setup free block cache.\n");
sb->nfreeblock = V7FS_MAX_FREEBLOCK;
for (i = V7FS_MAX_FREEBLOCK - 1, j = sb->datablock_start_sector; i >= 0;
i--, j++)
sb->freeblock[i] = j;
sb->total_freeblock = volume_size - sb->datablock_start_sector;
sb->modified = 1;
if ((error = v7fs_superblock_writeback(fs))) {
errno = error;
warn("Can't write back superblock.");
return error;
}
VPRINTF(4, "Setup whole freeblock list.\n");
progress(&(struct progress_arg){ .label = "freeblock list", .tick =
(volume_size - sb->datablock_start_sector) / PROGRESS_BAR_GRANULE});
blk = sb->freeblock[0];
while ((blk = make_freeblocklist(fs, blk, buf)))
continue;
#ifndef HAVE_NBTOOL_CONFIG_H
progress_done();
#endif
VPRINTF(4, "done.\n");
return 0;
}
int
v7fs_newfs(const struct v7fs_mount_device *mount, int32_t maxfile)
{
struct v7fs_self *fs;
v7fs_daddr_t ilist_size;
int error;
v7fs_daddr_t volume_size = mount->sectors;
if (volume_size > V7FS_DADDR_MAX + 1) {
warnx("volume size %d over v7fs limit %d. truncated.",
volume_size, V7FS_DADDR_MAX + 1);
volume_size = V7FS_DADDR_MAX + 1;
}
ilist_size = determine_ilist_size(volume_size, maxfile);
VPRINTF(1, "volume size=%d, ilist size=%d, endian=%d, NAME_MAX=%d\n",
volume_size, ilist_size, mount->endian, V7FS_NAME_MAX);
if ((error = v7fs_io_init(&fs, mount, V7FS_BSIZE))) {
errno = error;
warn("I/O setup failed.");
return error;
}
fs->endian = mount->endian;
v7fs_endian_init(fs);
if ((error = partition_check(fs))) {
return error;
}
if ((error = make_filesystem(fs, volume_size, ilist_size))) {
return error;
}
if ((error = make_root(fs))) {
return error;
}
v7fs_io_fini(fs);
return 0;
}