diff --git a/Makefile b/Makefile index efd51392..604473c6 100644 --- a/Makefile +++ b/Makefile @@ -69,7 +69,7 @@ everything-all-archs: # Initializing RamDisk $(INITRD): suball - (cd $(INITRDDIR) && ../mkinitrd/mkinitrd * -o ../$(INITRD)) + mkinitrd/mkinitrd initrd -o $(INITRD) # Statistics linecount: diff --git a/mkinitrd/.gitignore b/mkinitrd/.gitignore index f5631bac..deee3b80 100644 --- a/mkinitrd/.gitignore +++ b/mkinitrd/.gitignore @@ -1,3 +1,2 @@ mkinitrd -lsinitrd -catinitrd +initrdfs diff --git a/mkinitrd/Makefile b/mkinitrd/Makefile index 7cf1b6e5..fc7e53ac 100644 --- a/mkinitrd/Makefile +++ b/mkinitrd/Makefile @@ -1,12 +1,15 @@ +SORTIXKERNEL=../sortix +LIBMAXSI=../libmaxsi + CPPFLAGS=-I../sortix/include CXXFLAGS=-Wall -BINARIES=mkinitrd lsinitrd catinitrd +BINARIES=mkinitrd initrdfs all: $(BINARIES) -%: %.cpp - $(CXX) $(CPPFLAGS) $(CXXFLAGS) $< -o $@ +%: %.cpp crc32.cpp $(LIBMAXSI)/ioleast.cpp + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $< crc32.cpp $(LIBMAXSI)/ioleast.cpp -o $@ clean: rm -f $(BINARIES) diff --git a/mkinitrd/catinitrd.cpp b/mkinitrd/catinitrd.cpp deleted file mode 100644 index b0df228b..00000000 --- a/mkinitrd/catinitrd.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/****************************************************************************** - - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . - - catinitrd.cpp - Output files in a Sortix ramdisk. - -******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -bool writeall(int fd, const void* p, size_t size) -{ - const uint8_t* buffer = (const uint8_t*) p; - - size_t bytesWritten = 0; - - while ( bytesWritten < size ) - { - ssize_t written = write(fd, buffer + bytesWritten, size - bytesWritten); - if ( written < 0 ) { return false; } - bytesWritten += written; - } - - return true; -} - -bool readall(int fd, void* p, size_t size) -{ - uint8_t* buffer = (uint8_t*) p; - - size_t bytesread = 0; - - while ( bytesread < size ) - { - ssize_t justread = read(fd, buffer + bytesread, size - bytesread); - if ( justread <= 0 ) { return false; } - bytesread += justread; - } - - return true; -} - -void usage(int argc, char* argv[]) -{ - printf("usage: %s [OPTIONS] [files]\n", argv[0]); - printf("Options:\n"); - printf(" -q Surpress normal output\n"); - printf(" -v Be verbose\n"); - printf(" --usage Display this screen\n"); - printf(" --help Display this screen\n"); - printf(" --version Display version information\n"); -} - -void version() -{ - printf("catinitrd 0.1\n"); - printf("Copyright (C) 2011 Jonas 'Sortie' Termansen\n"); - printf("This is free software; see the source for copying conditions. There is NO\n"); - printf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); - printf("website: http://www.maxsi.org/software/sortix/\n"); -} - -bool verbose = false; - -int main(int argc, char* argv[]) -{ - if ( argc < 2 ) { usage(argc, argv); return 0; } - - const char* src = NULL; - - for ( int i = 1; i < argc; i++ ) - { - if ( strcmp(argv[i], "-q") == 0 ) - { - verbose = false; - argv[i] = NULL; - } - else if ( strcmp(argv[i], "-v") == 0 ) - { - verbose = true; - argv[i] = NULL; - } - else if ( strcmp(argv[i], "--usage") == 0 ) - { - usage(argc, argv); - return 0; - } - else if ( strcmp(argv[i], "--help") == 0 ) - { - usage(argc, argv); - return 0; - } - else if ( strcmp(argv[i], "--version") == 0 ) - { - version(); - return 0; - } - else if ( src == NULL ) - { - src = argv[i]; - argv[i] = NULL; - } - } - - int fd = open(src, O_RDONLY); - if ( fd < 0 ) { error(0, errno, "%s", src); return 1; } - - Sortix::InitRD::Header header; - if ( !readall(fd, &header, sizeof(header)) ) - { - error(0, errno, "read: %s", src); - close(fd); - return 1; - } - - if ( strcmp(header.magic, "sortix-initrd-1") != 0 ) - { - error(0, 0, "not a sortix ramdisk: %s", src); - close(fd); - return 1; - } - - int result = 0; - - for ( int i = 1; i < argc; i++ ) - { - if ( argv[i] == NULL ) { continue; } - - bool found = false; - - for ( uint32_t n = 0; n < header.numfiles; n++ ) - { - off_t fileheaderpos = sizeof(header) + n * sizeof(Sortix::InitRD::FileHeader); - if ( lseek(fd, fileheaderpos, SEEK_SET ) < 0 ) - { - error(0, errno, "seek: %s", src); - close(fd); - return 1; - } - - Sortix::InitRD::FileHeader fileheader; - if ( !readall(fd, &fileheader, sizeof(fileheader)) ) - { - error(0, errno, "read: %s", src); - close(fd); - return 1; - } - - if ( strcmp(argv[i], fileheader.name) != 0 ) { continue; } - - found = true; - - if ( lseek(fd, fileheader.offset, SEEK_SET ) < 0 ) - { - error(0, errno, "seek: %s", src); - close(fd); - return 1; - } - - const size_t BUFFER_SIZE = 16384UL; - uint8_t buffer[BUFFER_SIZE]; - - uint32_t filesize = fileheader.size; - uint32_t readsofar = 0; - while ( readsofar < filesize ) - { - uint32_t left = filesize-readsofar; - size_t toread = (left < BUFFER_SIZE) ? left : BUFFER_SIZE; - ssize_t bytesread = read(fd, buffer, toread); - if ( bytesread <= 0 ) - { - error(0, errno, "read: %s", src); - close(fd); - return 1; - } - - if ( !writeall(1, buffer, bytesread) ) - { - error(0, errno, "write: "); - close(fd); - return 1; - } - - readsofar += bytesread; - } - } - - if ( !found ) - { - result |= 1; - error(0, ENOENT, "%s", argv[i]); - } - } - - close(fd); - - return result; -} diff --git a/mkinitrd/crc32.cpp b/mkinitrd/crc32.cpp new file mode 100644 index 00000000..6898c7e4 --- /dev/null +++ b/mkinitrd/crc32.cpp @@ -0,0 +1,94 @@ +/******************************************************************************* + + COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2012. + COPYRIGHT(C) KRZYSZTOF DABROWSKI 1999, 2000. + COPYRIGHT(C) ElysiuM deeZine 1999, 2000. + Based on implementation by Finn Yannick Jacobs. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along + with Sortix. If not, see . + + crc32.cpp + Calculates a CRC32 Checksum on binary data. + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include "crc32.h" + +void GenerateCRC32Table(uint32_t tabel[256]) +{ + uint32_t poly = 0xEDB88320U; + for ( uint32_t i = 0; i < 256; i++ ) + { + uint32_t crc = i; + for ( uint32_t j = 8; 0 < j; j-- ) + { + if ( crc & 1 ) { crc = (crc >> 1) ^ poly; } + else { crc >>= 1; } + } + tabel[i] = crc; + } +} + +uint32_t ContinueCRC32(uint32_t tabel[256], uint32_t crc, uint8_t* block, + size_t size) +{ + for ( size_t i = 0; i < size; i++ ) + { + crc = ((crc >> 8) & 0x00FFFFFF) ^ tabel[(crc ^ *block++) & 0xFF]; + } + return crc; +} + +uint32_t FinishCRC32(uint32_t crc) +{ + return crc ^ 0xFFFFFFFF; +} + +uint32_t CRC32(uint8_t* block, size_t size) +{ + uint32_t tabel[256]; + GenerateCRC32Table(tabel); + return FinishCRC32(ContinueCRC32(tabel, CRC32_START_SEED, block, size)); +} + +bool CRC32File(uint32_t* result, const char* name, int fd, off_t offset, + off_t length) +{ + uint32_t tabel[256]; + GenerateCRC32Table(tabel); + uint32_t crc = CRC32_START_SEED; + const size_t BUFFER_SIZE = 16UL * 1024UL; + uint8_t buffer[BUFFER_SIZE]; + off_t sofar = 0; + ssize_t amount; + while ( sofar < length && + 0 < (amount = pread(fd, buffer, BUFFER_SIZE, offset + sofar)) ) + { + if ( length - sofar < amount ) { amount = length - sofar; } + crc = ContinueCRC32(tabel, crc, buffer, amount); + sofar += amount; + } + if ( amount < 0 ) { error(0, errno, "read: %s", name); return false; } + if ( sofar < length ) { error(0, EIO, "read: %s", name); return false; } + crc = FinishCRC32(crc); + *result = crc; + return true; +} diff --git a/mkinitrd/crc32.h b/mkinitrd/crc32.h new file mode 100644 index 00000000..34018606 --- /dev/null +++ b/mkinitrd/crc32.h @@ -0,0 +1,40 @@ +/******************************************************************************* + + COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2012. + COPYRIGHT(C) KRZYSZTOF DABROWSKI 1999, 2000. + COPYRIGHT(C) ElysiuM deeZine 1999, 2000. + Based on implementation by Finn Yannick Jacobs. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along + with Sortix. If not, see . + + crc32.h + Calculates a CRC32 Checksum on binary data. + +*******************************************************************************/ + +#ifndef CRC32_H +#define CRC32_H + +const uint32_t CRC32_START_SEED = 0xFFFFFFFF; +void GenerateCRC32Table(uint32_t tabel[256]); +uint32_t ContinueCRC32(uint32_t tabel[256], uint32_t crc, uint8_t* block, + size_t size); +uint32_t FinishCRC32(uint32_t crc); +uint32_t CRC32(uint8_t* block, size_t size); +bool CRC32File(uint32_t* result, const char* name, int fd, off_t offset, + off_t length); + +#endif diff --git a/mkinitrd/initrdfs.cpp b/mkinitrd/initrdfs.cpp new file mode 100644 index 00000000..38cc50a6 --- /dev/null +++ b/mkinitrd/initrdfs.cpp @@ -0,0 +1,349 @@ +/******************************************************************************* + + Copyright(C) Jonas 'Sortie' Termansen 2012. + + This file is part of Sortix. + + Sortix is free software: you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation, either version 3 of the License, or (at your option) any later + version. + + Sortix is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + details. + + You should have received a copy of the GNU General Public License along + with Sortix. If not, see . + + initrdfs.cpp + Provides access to filesystems in the Sortix kernel initrd format. + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "crc32.h" + +#if !defined(sortix) +__BEGIN_DECLS +size_t preadall(int fd, void* buf, size_t count, off_t off); +size_t preadleast(int fd, void* buf, size_t least, size_t max, off_t off); +size_t pwriteall(int fd, const void* buf, size_t count, off_t off); +size_t pwriteleast(int fd, const void* buf, size_t least, size_t max, off_t off); +size_t readall(int fd, void* buf, size_t count); +size_t readleast(int fd, void* buf, size_t least, size_t max); +size_t writeall(int fd, const void* buf, size_t count); +size_t writeleast(int fd, const void* buf, size_t least, size_t max); +__END_DECLS +#endif + +char* Substring(const char* str, size_t start, size_t length) +{ + char* result = (char*) malloc(length+1); + strncpy(result, str + start, length); + result[length] = 0; + return result; +} + +bool ReadSuperBlock(int fd, initrd_superblock_t* dest) +{ + return preadall(fd, dest, sizeof(*dest), 0) == sizeof(*dest); +} + +initrd_superblock_t* GetSuperBlock(int fd) +{ + size_t sbsize = sizeof(initrd_superblock_t); + initrd_superblock_t* sb = (initrd_superblock_t*) malloc(sbsize); + if ( !sb ) { return NULL; } + if ( !ReadSuperBlock(fd, sb) ) { free(sb); return NULL; } + return sb; +} + +bool ReadChecksum(int fd, initrd_superblock_t* sb, uint8_t* dest) +{ + uint32_t offset = sb->fssize - sb->sumsize; + return preadall(fd, dest, sb->sumsize, offset) == sb->sumsize; +} + +uint8_t* GetChecksum(int fd, initrd_superblock_t* sb) +{ + uint8_t* checksum = (uint8_t*) malloc(sb->sumsize); + if ( !checksum ) { return NULL; } + if ( !ReadChecksum(fd, sb, checksum) ) { free(checksum); return NULL; } + return checksum; +} + +bool ReadInode(int fd, initrd_superblock_t* sb, uint32_t ino, + initrd_inode_t* dest) +{ + uint32_t inodepos = sb->inodeoffset + sb->inodesize * ino; + return preadall(fd, dest, sizeof(*dest), inodepos) == sizeof(*dest); +} + +initrd_inode_t* GetInode(int fd, initrd_superblock_t* sb, uint32_t ino) +{ + initrd_inode_t* inode = (initrd_inode_t*) malloc(sizeof(initrd_inode_t)); + if ( !inode ) { return NULL; } + if ( !ReadInode(fd, sb, ino, inode) ) { free(inode); return NULL; } + return inode; +} + +initrd_inode_t* CloneInode(const initrd_inode_t* src) +{ + initrd_inode_t* result = (initrd_inode_t*) malloc(sizeof(*src)); + if ( !result ) { return NULL; } + memcpy(result, src, sizeof(*src)); + return result; +} + +bool ReadInodeData(int fd, initrd_superblock_t* sb, initrd_inode_t* inode, + uint8_t* dest, size_t size) +{ + if ( inode->size < size ) { errno = EINVAL; return false; } + return preadall(fd, dest, size, inode->dataoffset) == size; +} + +uint8_t* GetInodeData(int fd, initrd_superblock_t* sb, initrd_inode_t* inode, + size_t size) +{ + uint8_t* buf = (uint8_t*) malloc(size); + if ( !buf ) { return NULL; } + if ( !ReadInodeData(fd, sb, inode, buf, size) ) { free(buf); return NULL; } + return buf; +} + +uint8_t* GetInodeData(int fd, initrd_superblock_t* sb, initrd_inode_t* inode) +{ + return GetInodeData(fd, sb, inode, inode->size); +} + +uint32_t Traverse(int fd, initrd_superblock_t* sb, initrd_inode_t* inode, + const char* name) +{ + if ( !INITRD_S_ISDIR(inode->mode) ) { errno = ENOTDIR; return 0; } + uint8_t* direntries = GetInodeData(fd, sb, inode); + if ( !direntries ) { return 0; } + uint32_t result = 0; + uint32_t offset = 0; + while ( offset < inode->size ) + { + initrd_dirent_t* dirent = (initrd_dirent_t*) (direntries + offset); + if ( dirent->namelen && !strcmp(dirent->name, name) ) + { + result = dirent->inode; + break; + } + offset += dirent->reclen; + } + free(direntries); + if ( !result ) { errno = ENOENT; } + return result; +} + +bool CheckSumCRC32(const char* name, int fd, initrd_superblock_t* sb) +{ + uint32_t* checksump = (uint32_t*) GetChecksum(fd, sb); + if ( !checksump ) { return false; } + uint32_t checksum = *checksump; + free(checksump); + uint32_t amount = sb->fssize - sb->sumsize; + uint32_t filesum; + if ( !CRC32File(&filesum, name, fd, 0, amount) ) { return false; } + if ( checksum != filesum ) { errno = EILSEQ; return false; } + return true; +} + +bool CheckSum(const char* name, int fd, initrd_superblock_t* sb) +{ + switch ( sb->sumalgorithm ) + { + case INITRD_ALGO_CRC32: return CheckSumCRC32(name, fd, sb); + default: + fprintf(stderr, "Warning: unsupported checksum algorithm: %s\n", name); + return true; + } +} + +initrd_inode_t* ResolvePath(int fd, initrd_superblock_t* sb, + initrd_inode_t* inode, const char* path) +{ + if ( !path[0] ) { return CloneInode(inode); } + if ( path[0] == '/' ) + { + if ( !INITRD_S_ISDIR(inode->mode) ) { errno = ENOTDIR; return NULL; } + return ResolvePath(fd, sb, inode, path+1); + } + size_t elemlen = strcspn(path, "/"); + char* elem = Substring(path, 0, elemlen); + uint32_t ino = Traverse(fd, sb, inode, elem); + free(elem); + if ( !ino ) { return NULL; } + initrd_inode_t* child = GetInode(fd, sb, ino); + if ( !child ) { return NULL; } + if ( !path[elemlen] ) { return child; } + initrd_inode_t* result = ResolvePath(fd, sb, child, path + elemlen); + free(child); + return result; +} + +bool ListDirectory(int fd, initrd_superblock_t* sb, initrd_inode_t* dir, + bool all) +{ + if ( !INITRD_S_ISDIR(dir->mode) ) { errno = ENOTDIR; return false; } + uint8_t* direntries = GetInodeData(fd, sb, dir); + if ( !direntries ) { return false; } + uint32_t offset = 0; + while ( offset < dir->size ) + { + initrd_dirent_t* dirent = (initrd_dirent_t*) (direntries + offset); + if ( dirent->namelen && (all || dirent->name[0] != '.')) + { + printf("%s\n", dirent->name); + } + offset += dirent->reclen; + } + free(direntries); + return true; +} + +bool PrintFile(int fd, initrd_superblock_t* sb, initrd_inode_t* inode) +{ + if ( INITRD_S_ISDIR(inode->mode ) ) { errno = EISDIR; return false; } + uint32_t sofar = 0; + while ( sofar < inode->size ) + { + const size_t BUFFER_SIZE = 16UL * 1024UL; + uint8_t buffer[BUFFER_SIZE]; + uint32_t available = inode->size - sofar; + uint32_t count = available < BUFFER_SIZE ? available : BUFFER_SIZE; + if ( !ReadInodeData(fd, sb, inode, buffer, count) ) { return false; } + if ( writeall(1, buffer, count) != count ) { return false; } + sofar += count; + } + return true; +} + +void Usage(FILE* fp, const char* argv0) +{ + fprintf(fp, "usage: %s [--check] (ls | cat) \n", argv0); + fprintf(fp, "Accesses data in a Sortix kernel init ramdisk.\n"); +} + +void Help(FILE* fp, const char* argv0) +{ + Usage(fp, argv0); +} + +void Version(FILE* fp, const char* argv0) +{ + fprintf(fp, "initrdfs 0.2\n"); + fprintf(fp, "Copyright (C) 2012 Jonas 'Sortie' Termansen\n"); + fprintf(fp, "This is free software; see the source for copying conditions. There is NO\n"); + fprintf(fp, "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); + fprintf(fp, "website: http://www.maxsi.org/software/sortix/\n"); +} + +int main(int argc, char* argv[]) +{ + bool all = false; + bool check = false; + const char* argv0 = argv[0]; + if ( argc < 2 ) { Usage(stdout, argv0); exit(0); } + for ( int i = 1; i < argc; i++ ) + { + const char* arg = argv[i]; + if ( arg[0] != '-' ) { continue; } + argv[i] = NULL; + if ( !strcmp(arg, "--") ) { break; } + if ( !strcmp(arg, "--help") ) { Help(stdout, argv0); exit(0); } + if ( !strcmp(arg, "--usage") ) { Usage(stdout, argv0); exit(0); } + if ( !strcmp(arg, "--version") ) { Version(stdout, argv0); exit(0); } + if ( !strcmp(arg, "-a") ) { all = true; continue; } + if ( !strcmp(arg, "--check") ) { check = true; continue; } + fprintf(stderr, "%s: unknown option: %s\n", argv0, arg); + Usage(stderr, argv0); + exit(1); + } + + const char* initrd = NULL; + const char* cmd = NULL; + const char* path = NULL; + int args = 0; + for ( int i = 1; i < argc; i++ ) + { + if ( !argv[i] ) { continue; } + switch ( ++args ) + { + case 1: initrd = argv[i]; break; + case 2: cmd = argv[i]; break; + case 3: path = argv[i]; break; + } + } + + const char* errmsg = NULL; + if ( !errmsg && !initrd ) { errmsg = "no initrd specified"; } + if ( !errmsg && !cmd ) { errmsg = "no command specified"; } + if ( !errmsg && !path ) { errmsg = "no path specified"; } + if ( !errmsg && 3 < args ) { errmsg = "too many arguments"; } + + if ( errmsg ) + { + fprintf(stderr, "%s: %s\n", argv0, errmsg), + Usage(stderr, argv0); + exit(1); + } + + int fd = open(initrd, O_RDONLY); + if ( fd < 0 ) { error(1, errno, "open: %s", initrd); } + + initrd_superblock_t* sb = GetSuperBlock(fd); + if ( !sb ) { error(1, errno, "read: %s", initrd); } + + if ( check && !CheckSum(initrd, fd, sb) ) + { + error(1, errno, "checksum error: %s", initrd); + } + + if ( path[0] != '/' ) { error(1, ENOENT, "%s", path); } + + initrd_inode_t* root = GetInode(fd, sb, sb->root); + if ( !root ) { error(1, errno, "read: %s", initrd); } + + initrd_inode_t* inode = ResolvePath(fd, sb, root, path+1); + if ( !inode ) { error(1, errno, "%s", path); } + + free(root); + + if ( !strcmp(cmd, "cat") ) + { + if ( !PrintFile(fd, sb, inode) ) { error(1, errno, "%s", path); } + } + else if ( !strcmp(cmd, "ls") ) + { + initrd_inode_t* dir = inode; + if ( !ListDirectory(fd, sb, dir, all) ) { error(1, errno, "%s", path); } + } + else + { + fprintf(stderr, "%s: unrecognized command: %s", argv0, cmd); + exit(1); + } + + free(inode); + free(sb); + close(fd); + + return 0; +} + diff --git a/mkinitrd/lsinitrd.cpp b/mkinitrd/lsinitrd.cpp deleted file mode 100644 index 002c6efe..00000000 --- a/mkinitrd/lsinitrd.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/****************************************************************************** - - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. - - This file is part of Sortix. - - Sortix is free software: you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation, either version 3 of the License, or (at your option) any later - version. - - Sortix is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - details. - - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . - - lsinitrd.cpp - Lists the files in a Sortix ramdisk. - -******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -bool readall(int fd, void* p, size_t size) -{ - uint8_t* buffer = (uint8_t*) p; - - size_t bytesread = 0; - - while ( bytesread < size ) - { - ssize_t justread = read(fd, buffer + bytesread, size - bytesread); - if ( justread <= 0 ) { return false; } - bytesread += justread; - } - - return true; -} - -void usage(int argc, char* argv[]) -{ - printf("usage: %s [OPTIONS] \n", argv[0]); - printf("Options:\n"); - printf(" -q Surpress normal output\n"); - printf(" -v Be verbose\n"); - printf(" --usage Display this screen\n"); - printf(" --help Display this screen\n"); - printf(" --version Display version information\n"); -} - -void version() -{ - printf("lsinitrd 0.1\n"); - printf("Copyright (C) 2011 Jonas 'Sortie' Termansen\n"); - printf("This is free software; see the source for copying conditions. There is NO\n"); - printf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); - printf("website: http://www.maxsi.org/software/sortix/\n"); -} - -bool verbose = false; - -int listfiles(const char* filepath) -{ - int fd = open(filepath, O_RDONLY); - if ( fd < 0 ) { error(0, errno, "%s", filepath); return 1; } - - Sortix::InitRD::Header header; - if ( !readall(fd, &header, sizeof(header)) ) - { - error(0, errno, "read: %s", filepath); - close(fd); - return 1; - } - - if ( strcmp(header.magic, "sortix-initrd-1") != 0 ) - { - error(0, 0, "not a sortix ramdisk: %s", filepath); - close(fd); - return 1; - } - - for ( uint32_t i = 0; i < header.numfiles; i++ ) - { - Sortix::InitRD::FileHeader fileheader; - if ( !readall(fd, &fileheader, sizeof(fileheader)) ) - { - error(0, errno, "read: %s", filepath); - close(fd); - return 1; - } - - printf("%s\n", fileheader.name); - } - - close(fd); - - return 0; -} - -int main(int argc, char* argv[]) -{ - if ( argc < 2 ) { usage(argc, argv); return 0; } - - for ( int i = 1; i < argc; i++ ) - { - if ( strcmp(argv[i], "-q") == 0 ) - { - verbose = false; - argv[i] = NULL; - } - else if ( strcmp(argv[i], "-v") == 0 ) - { - verbose = true; - argv[i] = NULL; - } - else if ( strcmp(argv[i], "--usage") == 0 ) - { - usage(argc, argv); - return 0; - } - else if ( strcmp(argv[i], "--help") == 0 ) - { - usage(argc, argv); - return 0; - } - else if ( strcmp(argv[i], "--version") == 0 ) - { - version(); - return 0; - } - } - - int result = 0; - - for ( int i = 1; i < argc; i++ ) - { - if ( argv[i] == NULL ) { continue; } - - result |= listfiles(argv[i]); - } - - return result; -} diff --git a/mkinitrd/mkinitrd.cpp b/mkinitrd/mkinitrd.cpp index fdef0037..95e6a23d 100644 --- a/mkinitrd/mkinitrd.cpp +++ b/mkinitrd/mkinitrd.cpp @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2012. This file is part of Sortix. @@ -18,271 +18,408 @@ with Sortix. If not, see . mkinitrd.cpp - Produces a simple ramdisk meant for bootstrapping the Sortix kernel. + Produces a simple ramdisk filesystem readable by the Sortix kernel. -******************************************************************************/ +*******************************************************************************/ -#include -#include -#include #include #include -#include #include #include +#include +#include +#include +#include +#include +#include #include #include -bool writeall(int fd, const void* p, size_t size) +#include "crc32.h" + +#if !defined(sortix) +__BEGIN_DECLS +size_t preadall(int fd, void* buf, size_t count, off_t off); +size_t preadleast(int fd, void* buf, size_t least, size_t max, off_t off); +size_t pwriteall(int fd, const void* buf, size_t count, off_t off); +size_t pwriteleast(int fd, const void* buf, size_t least, size_t max, off_t off); +size_t readall(int fd, void* buf, size_t count); +size_t readleast(int fd, void* buf, size_t least, size_t max); +size_t writeall(int fd, const void* buf, size_t count); +size_t writeleast(int fd, const void* buf, size_t least, size_t max); +__END_DECLS +#endif + +uint32_t HostModeToInitRD(mode_t mode) { - const uint8_t* buffer = (const uint8_t*) p; + uint32_t result = mode & 0777; // Lower 9 bits per POSIX and tradition. + if ( S_ISVTX & mode ) { result |= INITRD_S_ISVTX; } + if ( S_ISSOCK(mode) ) { result |= INITRD_S_IFSOCK; } + if ( S_ISLNK(mode) ) { result |= INITRD_S_IFLNK; } + if ( S_ISREG(mode) ) { result |= INITRD_S_IFREG; } + if ( S_ISBLK(mode) ) { result |= INITRD_S_IFBLK; } + if ( S_ISDIR(mode) ) { result |= INITRD_S_IFDIR; } + if ( S_ISCHR(mode) ) { result |= INITRD_S_IFCHR; } + if ( S_ISFIFO(mode) ) { result |= INITRD_S_IFIFO; } + return result; +} - size_t bytesWritten = 0; +mode_t InitRDModeToHost(uint32_t mode) +{ + mode_t result = mode & 0777; // Lower 9 bits per POSIX and tradition. + if ( INITRD_S_ISVTX & mode ) { result |= S_ISVTX; } + if ( INITRD_S_ISSOCK(mode) ) { result |= S_IFSOCK; } + if ( INITRD_S_ISLNK(mode) ) { result |= S_IFLNK; } + if ( INITRD_S_ISREG(mode) ) { result |= S_IFREG; } + if ( INITRD_S_ISBLK(mode) ) { result |= S_IFBLK; } + if ( INITRD_S_ISDIR(mode) ) { result |= S_IFDIR; } + if ( INITRD_S_ISCHR(mode) ) { result |= S_IFCHR; } + if ( INITRD_S_ISFIFO(mode) ) { result |= S_IFIFO; } + return result; +} - while ( bytesWritten < size ) +struct Node; +struct DirEntry; + +struct DirEntry +{ + char* name; + Node* node; +}; + +struct Node +{ + char* path; + uint32_t ino; + uint32_t nlink; + size_t direntsused; + size_t direntslength; + DirEntry* dirents; + mode_t mode; + time_t ctime; + time_t mtime; +}; + +void FreeNode(Node* node) +{ + if ( 1 < node->nlink ) { node->nlink--; return; } + for ( size_t i = 0; i < node->direntsused; i++ ) { - ssize_t written = write(fd, buffer + bytesWritten, size - bytesWritten); - if ( written < 0 ) { return false; } - bytesWritten += written; + DirEntry* entry = node->dirents + i; + if ( strcmp(entry->name, ".") != 0 && strcmp(entry->name, "..") != 0 ) + { + FreeNode(entry->node); + } + free(entry->name); + } + free(node->dirents); + free(node->path); + free(node); +} + +Node* RecursiveSearch(const char* rootpath, uint32_t* ino, Node* parent = NULL) +{ + struct stat st; + if ( lstat(rootpath, &st) ) { perror(rootpath); return NULL; } + + Node* node = (Node*) calloc(1, sizeof(Node)); + if ( !node ) { return NULL; } + + node->mode = st.st_mode; + node->ino = (*ino)++; + node->ctime = st.st_ctime; + node->mtime = st.st_mtime; + + char* pathclone = strdup(rootpath); + if ( !pathclone ) { perror("strdup"); free(node); return NULL; } + + node->path = pathclone; + + if ( !S_ISDIR(st.st_mode) ) { return node; } + + DIR* dir = opendir(rootpath); + if ( !dir ) { perror(rootpath); FreeNode(node); return NULL; } + + size_t rootpathlen = strlen(rootpath); + + bool successful = true; + struct dirent* entry; + while ( (entry = readdir(dir)) ) + { + size_t namelen = strlen(entry->d_name); + size_t subpathlen = namelen + 1 + rootpathlen; + char* subpath = (char*) malloc(subpathlen+1); + if ( !subpath ) { perror("malloc"); successful = false; break; } + stpcpy(stpcpy(stpcpy(subpath, rootpath), "/"), entry->d_name); + + Node* child = NULL; + if ( !strcmp(entry->d_name, ".") ) { child = node; } + if ( !strcmp(entry->d_name, "..") ) { child = parent ? parent : node; } + if ( !child ) { child = RecursiveSearch(subpath, ino, node); } + free(subpath); + if ( !child ) { successful = false; break; } + + if ( node->direntsused == node->direntslength ) + { + size_t oldlength = node->direntslength; + size_t newlength = oldlength ? 2 * oldlength : 8; + size_t newsize = sizeof(DirEntry) * newlength; + DirEntry* newdirents = (DirEntry*) realloc(node->dirents, newsize); + if ( !newdirents ) { perror("realloc"); successful = false; break; } + node->dirents = newdirents; + node->direntslength = newlength; + } + + char* nameclone = strdup(entry->d_name); + if ( !nameclone ) { perror("strdup"); successful = false; break; } + + DirEntry* entry = node->dirents + node->direntsused++; + + entry->name = nameclone; + entry->node = child; + } + + closedir(dir); + if ( !successful ) { FreeNode(node); return NULL; } + return node; +} + +bool WriteNode(struct initrd_superblock* sb, int fd, const char* outputname, + Node* node) +{ { + uint32_t filesize = 0; + uint32_t origfssize = sb->fssize; + uint32_t dataoff = origfssize; + uint32_t filestart = dataoff; + + if ( S_ISLNK(node->mode) ) // Symbolic link + { + const size_t NAME_SIZE = 1024UL; + char name[NAME_SIZE]; + ssize_t namelen = readlink(node->path, name, NAME_SIZE); + if ( namelen < 0 ) { goto ioreadlink; } + filesize = (uint32_t) namelen; + if ( pwriteall(fd, name, filesize, dataoff) < filesize ) goto ioread; + dataoff += filesize; + } + else if ( S_ISREG(node->mode) ) // Regular file + { + int nodefd = open(node->path, O_RDONLY); + if ( nodefd < 0 ) { goto ioopen; } + const size_t BUFFER_SIZE = 16UL * 1024UL; + uint8_t buffer[BUFFER_SIZE]; + ssize_t amount; + while ( 0 < (amount = read(nodefd, buffer, BUFFER_SIZE)) ) + { + if ( pwriteall(fd, buffer, amount, dataoff) < (size_t) amount ) + { + close(nodefd); + goto iowrite; + } + dataoff += amount; + filesize += amount; + } + close(nodefd); + if ( amount < 0 ) { goto ioread; } + } + else if ( S_ISDIR(node->mode) ) // Directory + { + for ( size_t i = 0; i < node->direntsused; i++ ) + { + DirEntry* entry = node->dirents + i; + const char* name = entry->name; + size_t namelen = strlen(entry->name); + struct initrd_dirent dirent; + dirent.inode = entry->node->ino; + dirent.namelen = (uint16_t) namelen; + dirent.reclen = sizeof(dirent) + dirent.namelen + 1; + dirent.reclen += dirent.reclen % 4; // Align entries. + size_t entsize = sizeof(dirent); + ssize_t hdramt = pwriteall(fd, &dirent, entsize, dataoff); + ssize_t nameamt = pwriteall(fd, name, namelen+1, dataoff + entsize); + if ( hdramt < (ssize_t) entsize || nameamt < (ssize_t) (namelen+1) ) + goto iowrite; + filesize += dirent.reclen; + dataoff += dirent.reclen; + } + } + + struct initrd_inode inode; + inode.mode = HostModeToInitRD(node->mode); + inode.uid = 1; + inode.gid = 1; + inode.nlink = node->nlink; + inode.ctime = (uint64_t) node->ctime; + inode.mtime = (uint64_t) node->mtime; + inode.dataoffset = filestart; + inode.size = filesize; + + uint32_t inodepos = sb->inodeoffset + node->ino * sb->inodesize; + uint32_t inodesize = sizeof(inode); + if ( pwriteall(fd, &inode, inodesize, inodepos) < inodesize ) goto iowrite; + + uint32_t increment = dataoff - origfssize; + sb->fssize += increment; + + return true; +} + ioreadlink: + error(0, errno, "readlink: %s", node->path); + return false; + ioopen: + error(0, errno, "open: %s", node->path); + return false; + ioread: + error(0, errno, "read: %s", node->path); + return false; + iowrite: + error(0, errno, "write: %s", outputname); + return false; +} + +bool WriteNodeRecursive(struct initrd_superblock* sb, int fd, + const char* outputname, Node* node) +{ + if ( !WriteNode(sb, fd, outputname, node) ) { return false; } + + if ( !S_ISDIR(node->mode) ) { return true; } + + for ( size_t i = 0; i < node->direntsused; i++ ) + { + DirEntry* entry = node->dirents + i; + const char* name = entry->name; + Node* child = entry->node; + if ( !strcmp(name, ".") || !strcmp(name, ".." ) ) { continue; } + if ( !WriteNodeRecursive(sb, fd, outputname, child) ) { return false; } } return true; } -uint8_t ContinueChecksum(uint8_t checksum, const void* p, size_t size) +bool Format(const char* outputname, int fd, uint32_t inodecount, Node* root) { - const uint8_t* buffer = (const uint8_t*) p; - while ( size-- ) + struct initrd_superblock sb; + memset(&sb, 0, sizeof(sb)); + strncpy(sb.magic, "sortix-initrd-2", sizeof(sb.magic)); + sb.revision = 0; + sb.fssize = sizeof(sb); + sb.inodesize = sizeof(initrd_inode); + sb.inodeoffset = sizeof(sb); + sb.inodecount = inodecount; + sb.root = root->ino; + + uint32_t inodebytecount = sb.inodesize * sb.inodecount; + sb.fssize += inodebytecount; + + if ( !WriteNodeRecursive(&sb, fd, outputname, root) ) { return false; } + + uint32_t crcsize = sizeof(uint32_t); + sb.sumalgorithm = INITRD_ALGO_CRC32; + sb.sumsize = crcsize; + sb.fssize += sb.sumsize; + + if ( pwriteall(fd, &sb, sizeof(sb), 0) < sizeof(sb) ) { - checksum += *buffer++; + error(0, errno, "write: %s", outputname); + return false; } - return checksum; + + uint32_t checksize = sb.fssize - sb.sumsize; + uint32_t crc; + if ( !CRC32File(&crc, outputname, fd, 0, checksize) ) { return false; } + if ( pwriteall(fd, &crc, crcsize, checksize) < crcsize ) { return false; } + + return true; } -void usage(int argc, char* argv[]) +bool Format(const char* pathname, uint32_t inodecount, Node* root) { - printf("usage: %s [OPTIONS] \n", argv[0]); - printf("Options:\n"); - printf(" -o Write the ramdisk to this file\n"); - printf(" -q Surpress normal output\n"); - printf(" -v Be verbose\n"); - printf(" --usage Display this screen\n"); - printf(" --help Display this screen\n"); - printf(" --version Display version information\n"); + int fd = open(pathname, O_RDWR | O_CREAT | O_TRUNC, 0666); + bool result = Format(pathname, fd, inodecount, root); + close(fd); + return result; } -void version() +void Usage(FILE* fp, const char* argv0) { - printf("mkinitrd 0.1\n"); - printf("Copyright (C) 2011 Jonas 'Sortie' Termansen\n"); - printf("This is free software; see the source for copying conditions. There is NO\n"); - printf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); - printf("website: http://www.maxsi.org/software/sortix/\n"); + fprintf(fp, "usage: %s -o \n", argv0); + fprintf(fp, "Creates a init ramdisk for the Sortix kernel.\n"); } -bool verbose = false; +void Help(FILE* fp, const char* argv0) +{ + Usage(fp, argv0); +} + +void Version(FILE* fp, const char* argv0) +{ + fprintf(fp, "mkinitrd 0.2\n"); + fprintf(fp, "Copyright (C) 2012 Jonas 'Sortie' Termansen\n"); + fprintf(fp, "This is free software; see the source for copying conditions. There is NO\n"); + fprintf(fp, "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"); + fprintf(fp, "website: http://www.maxsi.org/software/sortix/\n"); +} int main(int argc, char* argv[]) { + const char* argv0 = argv[0]; + if ( argc < 2 ) { Usage(stdout, argv0); exit(0); } const char* dest = NULL; - - if ( argc < 2 ) { usage(argc, argv); return 0; } - - uint32_t numfiles = 0; - for ( int i = 1; i < argc; i++ ) { - if ( strcmp(argv[i], "-o") == 0 ) + int argsleft = argc - i - 1; + const char* arg = argv[i]; + if ( arg[0] != '-' ) { continue; } + argv[i] = NULL; + if ( !strcmp(arg, "--") ) { break; } + if ( !strcmp(arg, "--help") ) { Help(stdout, argv0); exit(0); } + if ( !strcmp(arg, "--usage") ) { Usage(stdout, argv0); exit(0); } + if ( !strcmp(arg, "--version") ) { Version(stdout, argv0); exit(0); } + if ( !strcmp(arg, "-o") || !strcmp(arg, "--output") ) { - if ( i + 1 < argc ) + if ( argsleft < 1 ) { - dest = argv[i+1]; - argv[i+1] = NULL; + fprintf(stderr, "No output file specified\n"); + Usage(stderr, argv0); + exit(1); } - argv[i] = NULL; - i++; - } - else if ( strcmp(argv[i], "-q") == 0 ) - { - verbose = false; - argv[i] = NULL; - } - else if ( strcmp(argv[i], "-v") == 0 ) - { - verbose = true; - argv[i] = NULL; - } - else if ( strcmp(argv[i], "--usage") == 0 ) - { - usage(argc, argv); - return 0; - } - else if ( strcmp(argv[i], "--help") == 0 ) - { - usage(argc, argv); - return 0; - } - else if ( strcmp(argv[i], "--version") == 0 ) - { - version(); - return 0; - } - else - { - numfiles++; + dest = argv[++i]; argv[i] = NULL; + continue; } + fprintf(stderr, "%s: unknown option: %s\n", argv0, arg); + Usage(stderr, argv0); + exit(1); } - if ( dest == NULL ) - { - fprintf(stderr, "%s: no output file specified\n", argv[0]); - return 0; - } - - int fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); - if ( fd < 0 ) - { - error(0, errno, "%s", dest); - return 1; - } - - // Keep track of the file checksum. - Sortix::InitRD::Trailer trailer; - trailer.sum = 0; - - // Write the initrd headers. - Sortix::InitRD::Header header; - memset(&header, 0, sizeof(header)); - strcpy(header.magic, "sortix-initrd-1"); - header.numfiles = numfiles; - trailer.sum = ContinueChecksum(trailer.sum, &header, sizeof(header)); - if ( !writeall(fd, &header, sizeof(header)) ) - { - error(0, errno, "write: %s", dest); - close(fd); - unlink(dest); - return 1; - } - - uint32_t fileoffset = sizeof(header) + numfiles * sizeof(Sortix::InitRD::FileHeader); - - uint32_t filenum = 0; + const char* rootstr = NULL; + int args = 0; for ( int i = 1; i < argc; i++ ) { - const char* file = argv[i]; - - if ( file == NULL ) { continue; } - - Sortix::InitRD::FileHeader fileheader; - memset(&fileheader, 0, sizeof(fileheader)); - - if ( sizeof(fileheader.name) - 1 < strlen(file) ) - { - fprintf(stderr, "%s: file name is too long: %s\n", argv[0], file); - close(fd); - unlink(dest); - return 1; - } - - int filefd = open(file, O_RDONLY); - if ( filefd < 0 ) - { - error(0, errno, "%s", file); - close(fd); - unlink(dest); - return 1; - } - - if ( verbose ) - { - fprintf(stderr, "%s\n", file); - } - - struct stat st; - if ( fstat(filefd, &st) != 0 ) - { - error(0, errno, "stat: %s", file); - close(fd); - unlink(dest); - return 1; - } - - uint32_t filesize = st.st_size; - fileheader.permissions = 0755; - fileheader.size = filesize; - fileheader.owner = 1; - fileheader.group = 1; - fileheader.offset = fileoffset; - strcpy(fileheader.name, file); - - off_t fileheaderpos = sizeof(header) + filenum * sizeof(Sortix::InitRD::FileHeader); - if ( lseek(fd, fileheaderpos, SEEK_SET ) < 0 ) - { - error(0, errno, "seek: %s", dest); - close(fd); - unlink(dest); - return 1; - } - - trailer.sum = ContinueChecksum(trailer.sum, &fileheader, sizeof(fileheader)); - if ( !writeall(fd, &fileheader, sizeof(fileheader)) ) - { - error(0, errno, "write: %s", dest); - close(fd); - unlink(dest); - return 1; - } - - if ( lseek(fd, fileoffset, SEEK_SET ) < 0 ) - { - error(0, errno, "seek: %s", dest); - close(fd); - unlink(dest); - return 1; - } - - const size_t BUFFER_SIZE = 16384UL; - uint8_t buffer[BUFFER_SIZE]; - - uint32_t readsofar = 0; - while ( readsofar < filesize ) - { - uint32_t left = filesize-readsofar; - size_t toread = (left < BUFFER_SIZE) ? left : BUFFER_SIZE; - ssize_t bytesread = read(filefd, buffer, toread); - if ( bytesread <= 0 ) - { - error(0, errno, "read: %s", file); - close(fd); - unlink(dest); - return 1; - } - - trailer.sum = ContinueChecksum(trailer.sum, &buffer, bytesread); - if ( !writeall(fd, buffer, bytesread) ) - { - error(0, errno, "write: %s", dest); - close(fd); - unlink(dest); - return 1; - } - - readsofar += bytesread; - } - - fileoffset += filesize; - filenum++; - - close(filefd); + if ( !argv[i] ) { continue; } + args++; + rootstr = argv[i]; } - if ( !writeall(fd, &trailer, sizeof(trailer)) ) + const char* errmsg = NULL; + if ( !errmsg && args < 1 ) { errmsg = "no root specified"; } + if ( !errmsg && 1 < args ) { errmsg = "too many roots"; } + if ( !errmsg && !dest ) { errmsg = "no destination specified"; } + + if ( errmsg ) { - error(0, errno, "write: %s", dest); - close(fd); - unlink(dest); - return 1; + fprintf(stderr, "%s: %s\n", argv0, errmsg), + Usage(stderr, argv0); + exit(1); } + uint32_t inodecount = 1; + Node* root = RecursiveSearch(rootstr, &inodecount); + if ( !root ) { exit(1); } + + if ( !Format(dest, inodecount, root) ) { exit(1); } + + FreeNode(root); + return 0; } + diff --git a/sortix/fs/initfs.cpp b/sortix/fs/initfs.cpp index c5bc0558..941788e6 100644 --- a/sortix/fs/initfs.cpp +++ b/sortix/fs/initfs.cpp @@ -1,6 +1,6 @@ /****************************************************************************** - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -141,11 +141,13 @@ namespace Sortix typedef Device DevDirectory; public: - DevInitFSDir(); + DevInitFSDir(uint32_t dir); virtual ~DevInitFSDir(); private: size_t position; + uint32_t dir; + uint32_t numfiles; public: virtual void Rewind(); @@ -153,9 +155,11 @@ namespace Sortix }; - DevInitFSDir::DevInitFSDir() + DevInitFSDir::DevInitFSDir(uint32_t dir) { - position = 0; + this->position = 0; + this->dir = dir; + this->numfiles = InitRD::GetNumFiles(dir); } DevInitFSDir::~DevInitFSDir() @@ -170,17 +174,20 @@ namespace Sortix int DevInitFSDir::Read(sortix_dirent* dirent, size_t available) { if ( available <= sizeof(sortix_dirent) ) { return -1; } - if ( InitRD::GetNumFiles() <= position ) + if ( numfiles <= position ) { dirent->d_namelen = 0; dirent->d_name[0] = 0; return 0; } - const char* name = InitRD::GetFilename(position); + const char* name = InitRD::GetFilename(dir, position); size_t namelen = String::Length(name); size_t needed = sizeof(sortix_dirent) + namelen + 1; + // Oh right, the kernel is stupid and doesn't support dot and dotdot. + if ( name[0] == '.' ) { position++; return Read(dirent, available); } + if ( available < needed ) { dirent->d_namelen = needed; @@ -210,13 +217,16 @@ namespace Sortix if ( !path[0] || (path[0] == '/' && !path[1]) ) { if ( lowerflags != O_SEARCH ) { Error::Set(EISDIR); return NULL; } - return new DevInitFSDir(); + return new DevInitFSDir(InitRD::Root()); } if ( *path++ != '/' ) { Error::Set(ENOENT); return NULL; } - const byte* buffer = InitRD::Open(path, &buffersize); - if ( !buffer ) { Error::Set(ENOENT); return NULL; } + uint32_t ino = InitRD::Traverse(InitRD::Root(), path); + if ( !ino ) { return NULL; } + + const byte* buffer = InitRD::Open(ino, &buffersize); + if ( !buffer ) { return NULL; } if ( lowerflags == O_SEARCH ) { Error::Set(ENOTDIR); return NULL; } if ( lowerflags != O_RDONLY ) { Error::Set(EROFS); return NULL; } diff --git a/sortix/include/sortix/initrd.h b/sortix/include/sortix/initrd.h index 6176995b..db370157 100644 --- a/sortix/include/sortix/initrd.h +++ b/sortix/include/sortix/initrd.h @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2012. This file is part of Sortix. @@ -14,46 +14,89 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . initrd.h - Declares the structure of the Sortix ramdisk. + The Sortix init ramdisk filesystem format. -******************************************************************************/ +*******************************************************************************/ #ifndef SORTIX_INITRD_H #define SORTIX_INITRD_H -namespace Sortix +#include + +__BEGIN_DECLS + +#define INITRD_ALGO_CRC32 0 + +#define INITRD_S_IXOTH 01 +#define INITRD_S_IWOTH 02 +#define INITRD_S_IROTH 03 +#define INITRD_S_IRWXO 07 +#define INITRD_S_IXGRP 010 +#define INITRD_S_IWGRP 020 +#define INITRD_S_IRGRP 040 +#define INITRD_S_IRWXG 070 +#define INITRD_S_IXUSR 0100 +#define INITRD_S_IWUSR 0200 +#define INITRD_S_IRUSR 0400 +#define INITRD_S_IRWXU 0700 +#define INITRD_S_IFMT 0xF000 +#define INITRD_S_IFSOCK 0xC000 +#define INITRD_S_IFLNK 0xA000 +#define INITRD_S_IFREG 0x8000 +#define INITRD_S_IFBLK 0x6000 +#define INITRD_S_IFDIR 0x4000 +#define INITRD_S_IFCHR 0x2000 +#define INITRD_S_IFIFO 0x1000 +/* Intentionally not part of Sortix. */ +/*#define INITRD_S_ISUID 0x0800 */ +/*#define INITRD_S_ISGID 0x0400 */ +#define INITRD_S_ISVTX 0x0200 +#define INITRD_S_ISSOCK(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFSOCK) +#define INITRD_S_ISLNK(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFLNK) +#define INITRD_S_ISREG(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFREG) +#define INITRD_S_ISBLK(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFBLK) +#define INITRD_S_ISDIR(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFDIR) +#define INITRD_S_ISCHR(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFCHR) +#define INITRD_S_ISFIFO(mode) ((mode & INITRD_S_IFMT) == INITRD_S_IFIFO) + +typedef struct initrd_superblock { - namespace InitRD - { - struct Header; - struct FileHeader; + char magic[16]; // "sortix-initrd-2" + uint32_t fssize; + uint32_t revision; + uint32_t inodesize; + uint32_t inodecount; + uint32_t inodeoffset; + uint32_t root; + uint32_t sumalgorithm; + uint32_t sumsize; +} initrd_superblock_t; - struct Header - { - char magic[16]; // Contains "sortix-initrd-1" - uint32_t numfiles; - // FileHeader[numfiles]; - }; +typedef struct initrd_inode +{ + uint32_t mode; + uint32_t uid; + uint32_t gid; + uint32_t nlink; + uint64_t ctime; + uint64_t mtime; + uint32_t dataoffset; + uint32_t size; +} initrd_inode_t; - struct FileHeader - { - mode_t permissions; - uid_t owner; - gid_t group; - uint32_t size; - uint32_t offset; // where the physical data is located. - char name[128]; - }; +typedef struct initrd_dirent +{ + uint32_t inode; + uint16_t reclen; + uint16_t namelen; + char name[0]; +} initrd_dirent_t; - struct Trailer - { - uint8_t sum; // sum of all bytes but the trailer. - }; - } -} +__END_DECLS #endif + diff --git a/sortix/include/sortix/stat.h b/sortix/include/sortix/stat.h index 12a37afd..0e88bd7b 100644 --- a/sortix/include/sortix/stat.h +++ b/sortix/include/sortix/stat.h @@ -1,6 +1,6 @@ /******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2012. + Copyright(C) Jonas 'Sortie' Termansen 2012. This file is part of Sortix. @@ -59,17 +59,25 @@ struct stat #define S_IWUSR 0200 #define S_IRUSR 0400 #define S_IRWXU 0700 -#define S_IFDIR 0040000 -#define S_IFBLK 0060000 -#define S_IFREG 0100000 -#define S_IFLNK 0200000 /* not the same as in Linux */ -/* TODO: Define the other useful values and implement their features. */ - -#define S_ISBLK(m) ((m) & S_IFBLK) -#define S_ISDIR(m) ((m) & S_IFDIR) -#define S_ISREG(m) ((m) & S_IFREG) -#define S_ISLNK(m) ((m) & S_IFLNK) -/* TODO: Define the other useful macros and implement their features. */ +#define S_IFMT 0xF000 +#define S_IFSOCK 0xC000 +#define S_IFLNK 0xA000 +#define S_IFREG 0x8000 +#define S_IFBLK 0x6000 +#define S_IFDIR 0x4000 +#define S_IFCHR 0x2000 +#define S_IFIFO 0x1000 +/* Intentionally not part of Sortix. */ +/*#define S_ISUID 0x0800 */ +/*#define S_ISGID 0x0400 */ +#define S_ISVTX 0x0200 +#define S_ISSOCK(mode) ((mode & S_IFMT) == S_IFSOCK) +#define S_ISLNK(mode) ((mode & S_IFMT) == S_IFLNK) +#define S_ISREG(mode) ((mode & S_IFMT) == S_IFREG) +#define S_ISBLK(mode) ((mode & S_IFMT) == S_IFBLK) +#define S_ISDIR(mode) ((mode & S_IFMT) == S_IFDIR) +#define S_ISCHR(mode) ((mode & S_IFMT) == S_IFCHR) +#define S_ISFIFO(mode) ((mode & S_IFMT) == S_IFIFO) __END_DECLS diff --git a/sortix/initrd.cpp b/sortix/initrd.cpp index 527acfd3..e24f67f5 100644 --- a/sortix/initrd.cpp +++ b/sortix/initrd.cpp @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,119 +14,222 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . initrd.cpp - Declares the structure of the Sortix ramdisk. + Provides low-level access to a Sortix init ramdisk. -******************************************************************************/ +*******************************************************************************/ #include +#include +#include #include -#include "initrd.h" +#include #include #include +#include +#include "initrd.h" #include "syscall.h" -#include - -#include // DEBUG using namespace Maxsi; -namespace Sortix +namespace Sortix { +namespace InitRD { + +uint8_t* initrd; +size_t initrdsize; +const initrd_superblock_t* sb; + +static uint32_t HostModeToInitRD(mode_t mode) { - namespace InitRD + uint32_t result = mode & 0777; // Lower 9 bits per POSIX and tradition. + if ( S_ISVTX & mode ) { result |= INITRD_S_ISVTX; } + if ( S_ISSOCK(mode) ) { result |= INITRD_S_IFSOCK; } + if ( S_ISLNK(mode) ) { result |= INITRD_S_IFLNK; } + if ( S_ISREG(mode) ) { result |= INITRD_S_IFREG; } + if ( S_ISBLK(mode) ) { result |= INITRD_S_IFBLK; } + if ( S_ISDIR(mode) ) { result |= INITRD_S_IFDIR; } + if ( S_ISCHR(mode) ) { result |= INITRD_S_IFCHR; } + if ( S_ISFIFO(mode) ) { result |= INITRD_S_IFIFO; } + return result; +} + +static mode_t InitRDModeToHost(uint32_t mode) +{ + mode_t result = mode & 0777; // Lower 9 bits per POSIX and tradition. + if ( INITRD_S_ISVTX & mode ) { result |= S_ISVTX; } + if ( INITRD_S_ISSOCK(mode) ) { result |= S_IFSOCK; } + if ( INITRD_S_ISLNK(mode) ) { result |= S_IFLNK; } + if ( INITRD_S_ISREG(mode) ) { result |= S_IFREG; } + if ( INITRD_S_ISBLK(mode) ) { result |= S_IFBLK; } + if ( INITRD_S_ISDIR(mode) ) { result |= S_IFDIR; } + if ( INITRD_S_ISCHR(mode) ) { result |= S_IFCHR; } + if ( INITRD_S_ISFIFO(mode) ) { result |= S_IFIFO; } + return result; +} + +uint32_t Root() +{ + return sb->root; +} + +static const initrd_inode_t* GetInode(uint32_t inode) +{ + if ( sb->inodecount <= inode ) { Error::Set(EINVAL); return NULL; } + uint32_t pos = sb->inodeoffset + sb->inodesize * inode; + return (const initrd_inode_t*) (initrd + pos); +} + +bool Stat(uint32_t ino, struct stat* st) +{ + const initrd_inode_t* inode = GetInode(ino); + if ( !inode ) { return false; } + st->st_ino = ino; + st->st_mode = HostModeToInitRD(inode->mode); + st->st_nlink = inode->nlink; + st->st_uid = inode->uid; + st->st_gid = inode->gid; + st->st_size = inode->size; + st->st_atime = inode->mtime; + st->st_ctime = inode->ctime; + st->st_mtime = inode->mtime; + st->st_blksize = 1; + st->st_blocks = inode->size; + return true; +} + +uint8_t* Open(uint32_t ino, size_t* size) +{ + const initrd_inode_t* inode = GetInode(ino); + if ( !inode ) { return NULL; } + *size = inode->size; + return initrd + inode->dataoffset; +} + +uint32_t Traverse(uint32_t ino, const char* name) +{ + const initrd_inode_t* inode = GetInode(ino); + if ( !inode ) { return 0; } + if ( !INITRD_S_ISDIR(inode->mode) ) { Error::Set(ENOTDIR); return 0; } + uint32_t offset = 0; + while ( offset < inode->size ) { - byte* initrd; - size_t initrdsize; -#ifdef JSSORTIX - // JSVM never tells JSSortix how big the initrd is! - const bool CHECK_CHECKSUM = false; -#else - const bool CHECK_CHECKSUM = true; -#endif - - size_t GetNumFiles() + uint32_t pos = inode->dataoffset + offset; + const initrd_dirent* dirent = (const initrd_dirent*) (initrd + pos); + if ( dirent->namelen && !String::Compare(dirent->name, name) ) { - Header* header = (Header*) initrd; - return header->numfiles; - } - const char* GetFilename(size_t index) - { - Header* header = (Header*) initrd; - if ( index >= header->numfiles ) { return NULL; } - FileHeader* fhtbl = (FileHeader*) (initrd + sizeof(Header)); - FileHeader* fileheader = &(fhtbl[index]); - return fileheader->name; + return dirent->inode; } + offset += dirent->reclen; + } + Error::Set(ENOENT); + return 0; +} - uint8_t ContinueChecksum(uint8_t checksum, const void* p, size_t size) - { - const uint8_t* buffer = (const uint8_t*) p; - while ( size-- ) - { - checksum += *buffer++; - } - return checksum; - } - void CheckSum() - { - Trailer* trailer = (Trailer*) (initrd + initrdsize - sizeof(Trailer)); - uint8_t checksum = ContinueChecksum(0, initrd, initrdsize - sizeof(Trailer)); - if ( trailer->sum != checksum ) - { - PanicF("InitRD Checksum failed: the ramdisk may have been " - "corrupted by the bootloader: Got %u instead of %u " - "when checking the ramdisk at 0x%p + 0x%zx bytes\n", - checksum, trailer->sum, initrd, initrdsize); - } - } +const char* GetFilename(uint32_t dir, size_t index) +{ + const initrd_inode_t* inode = GetInode(dir); + if ( !inode ) { return 0; } + if ( !INITRD_S_ISDIR(inode->mode) ) { Error::Set(ENOTDIR); return 0; } + uint32_t offset = 0; + while ( offset < inode->size ) + { + uint32_t pos = inode->dataoffset + offset; + const initrd_dirent* dirent = (const initrd_dirent*) (initrd + pos); + if ( index-- == 0 ) { return dirent->name; } + offset += dirent->reclen; + } + Error::Set(EINVAL); + return NULL; +} - void Init(addr_t phys, size_t size) - { - // First up, map the initrd onto the kernel's address space. - addr_t virt = Memory::GetInitRD(); - size_t amount = 0; - while ( amount < size ) - { - if ( !Memory::MapKernel(phys + amount, virt + amount) ) - { - Panic("Unable to map the init ramdisk into virtual memory"); - } - amount += 0x1000UL; - } +size_t GetNumFiles(uint32_t dir) +{ + const initrd_inode_t* inode = GetInode(dir); + if ( !inode ) { return 0; } + if ( !INITRD_S_ISDIR(inode->mode) ) { Error::Set(ENOTDIR); return 0; } + uint32_t offset = 0; + size_t numentries = 0; + while ( offset < inode->size ) + { + uint32_t pos = inode->dataoffset + offset; + const initrd_dirent* dirent = (const initrd_dirent*) (initrd + pos); + numentries++; + offset += dirent->reclen; + } + return numentries; +} - Memory::Flush(); - - initrd = (byte*) virt; - initrdsize = size; - if ( size < sizeof(Header) ) { PanicF("initrd.cpp: initrd is too small"); } - Header* header = (Header*) initrd; - if ( String::Compare(header->magic, "sortix-initrd-1") != 0 ) { PanicF("initrd.cpp: invalid magic value in the initrd"); } - size_t sizeneeded = sizeof(Header) + header->numfiles * sizeof(FileHeader); - if ( size < sizeneeded ) { PanicF("initrd.cpp: initrd is too small"); } - // TODO: We need to do more validation here! - - if ( CHECK_CHECKSUM ) { CheckSum(); } - } - - byte* Open(const char* filepath, size_t* size) - { - Header* header = (Header*) initrd; - FileHeader* fhtbl = (FileHeader*) (initrd + sizeof(Header)); - for ( uint32_t i = 0; i < header->numfiles; i++ ) - { - FileHeader* fileheader = &(fhtbl[i]); - - if ( String::Compare(filepath, fileheader->name) != 0 ) { continue; } - - *size = fileheader->size; - return initrd + fileheader->offset; - } - - return NULL; - } +void CheckSum() +{ + uint32_t amount = sb->fssize - sb->sumsize; + uint8_t* filesum = initrd + amount; + if ( sb->sumalgorithm != INITRD_ALGO_CRC32 ) + { + Log::PrintF("Warning: InitRD checksum algorithm not supported\n"); + return; + } + uint32_t crc32 = *((uint32_t*) filesum); + uint32_t filecrc32 = CRC32::Hash(initrd, amount); + if ( crc32 != filecrc32 ) + { + PanicF("InitRD had checksum %X, expected %X: this means the ramdisk " + "may have been corrupted by the bootloader.", filecrc32, crc32); } } + +void Init(addr_t phys, size_t size) +{ + // First up, map the initrd onto the kernel's address space. + addr_t virt = Memory::GetInitRD(); + size_t amount = 0; + while ( amount < size ) + { + if ( !Memory::MapKernel(phys + amount, virt + amount) ) + { + Panic("Unable to map the init ramdisk into virtual memory"); + } + amount += 0x1000UL; + } + + Memory::Flush(); + + initrd = (uint8_t*) virt; + initrdsize = size; + + if ( size < sizeof(*sb) ) { PanicF("initrd is too small"); } + sb = (const initrd_superblock_t*) initrd; + + if ( !String::StartsWith(sb->magic, "sortix-initrd") ) + { + Panic("Invalid magic value in initrd. This means the ramdisk may have " + "been corrupted by the bootloader, or that an incompatible file " + "has been passed to the kernel."); + } + + if ( String::Compare(sb->magic, "sortix-initrd-1") == 0 ) + { + Panic("Sortix initrd format version 1 is no longer supported."); + } + + if ( String::Compare(sb->magic, "sortix-initrd-2") != 0 ) + { + Panic("The initrd has a format that isn't supported. Perhaps it is " + "too new? Try downgrade or regenerate the initrd."); + } + + if ( size < sb->fssize ) + { + PanicF("The initrd said it is %u bytes, but the kernel was only passed " + "%zu bytes by the bootloader, which is not enough.", sb->fssize, + size); + } + + CheckSum(); +} + +} // namespace InitRD +} // namespace Sortix diff --git a/sortix/initrd.h b/sortix/initrd.h index a3d9826d..a5531d65 100644 --- a/sortix/initrd.h +++ b/sortix/initrd.h @@ -1,6 +1,6 @@ -/****************************************************************************** +/******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -14,26 +14,29 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along - with Sortix. If not, see . + You should have received a copy of the GNU General Public License along with + Sortix. If not, see . initrd.h - Declares the structure of the Sortix ramdisk. + Provides low-level access to a Sortix init ramdisk. -******************************************************************************/ +*******************************************************************************/ #ifndef SORTIX_INITRD_KERNEL_H #define SORTIX_INITRD_KERNEL_H -namespace Sortix -{ - namespace InitRD - { - void Init(addr_t phys, size_t size); - byte* Open(const char* filepath, size_t* size); - const char* GetFilename(size_t index); - size_t GetNumFiles(); - } -} +namespace Sortix { +namespace InitRD { + +void Init(addr_t phys, size_t size); +uint32_t Root(); +bool Stat(uint32_t inode, struct stat* st); +uint8_t* Open(uint32_t inode, size_t* size); +uint32_t Traverse(uint32_t inode, const char* name); +const char* GetFilename(uint32_t dir, size_t index); +size_t GetNumFiles(uint32_t dir); + +} // namespace InitRD +} // namespace Sortix #endif diff --git a/sortix/kernel.cpp b/sortix/kernel.cpp index 50b1bb1a..9d7b33e0 100644 --- a/sortix/kernel.cpp +++ b/sortix/kernel.cpp @@ -1,6 +1,6 @@ /******************************************************************************* - COPYRIGHT(C) JONAS 'SORTIE' TERMANSEN 2011, 2012. + Copyright(C) Jonas 'Sortie' Termansen 2011, 2012. This file is part of Sortix. @@ -197,6 +197,7 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) // time to load the initial user-space programs and start execution of // the actual operating system. + uint32_t inode; byte* program; size_t programsize; @@ -214,7 +215,9 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) idle->addrspace = idleaddrspace; Memory::SwitchAddressSpace(idleaddrspace); Scheduler::SetDummyThreadOwner(idle); - program = InitRD::Open("idle", &programsize); + inode = InitRD::Traverse(InitRD::Root(), "idle"); + if ( inode == NULL ) { PanicF("initrd did not contain 'idle'"); } + program = InitRD::Open(inode, &programsize); if ( program == NULL ) { PanicF("initrd did not contain 'idle'"); } addr_t idlestart = ELF::Construct(idle, program, programsize); if ( !idlestart ) { Panic("could not construct ELF image for idle process"); } @@ -228,7 +231,9 @@ extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo) init->addrspace = initaddrspace; Memory::SwitchAddressSpace(initaddrspace); Scheduler::SetDummyThreadOwner(init); - program = InitRD::Open("init", &programsize); + inode = InitRD::Traverse(InitRD::Root(), "init"); + if ( inode == NULL ) { PanicF("initrd did not contain 'init'"); } + program = InitRD::Open(inode, &programsize); if ( program == NULL ) { PanicF("initrd did not contain 'init'"); } addr_t initstart = ELF::Construct(init, program, programsize); if ( !initstart ) { Panic("could not construct ELF image for init process"); }