Add iso9660 filesystem implementation.
This commit is contained in:
parent
fd58b62068
commit
2100eb369d
29 changed files with 3323 additions and 3 deletions
1
Makefile
1
Makefile
|
@ -24,6 +24,7 @@ games \
|
|||
hostname \
|
||||
ifconfig \
|
||||
init \
|
||||
iso9660 \
|
||||
kblayout \
|
||||
kblayout-compiler \
|
||||
login \
|
||||
|
|
2
iso9660/.gitignore
vendored
Normal file
2
iso9660/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
iso9660fs
|
||||
*.o
|
33
iso9660/Makefile
Normal file
33
iso9660/Makefile
Normal file
|
@ -0,0 +1,33 @@
|
|||
include ../build-aux/platform.mak
|
||||
include ../build-aux/compiler.mak
|
||||
include ../build-aux/version.mak
|
||||
include ../build-aux/dirs.mak
|
||||
|
||||
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
|
||||
CXXFLAGS?=$(OPTLEVEL)
|
||||
|
||||
CPPFLAGS:=$(CPPFLAGS) -DVERSIONSTR=\"$(VERSION)\"
|
||||
CXXFLAGS:=$(CXXFLAGS) -Wall -Wextra -fno-exceptions -fno-rtti -fcheck-new
|
||||
|
||||
LIBS:=$(LIBS)
|
||||
|
||||
ifeq ($(HOST_IS_SORTIX),0)
|
||||
LIBS:=$(LIBS) -lfuse
|
||||
CPPFLAGS:=$(CPPFLAGS) -D_FILE_OFFSET_BITS=64
|
||||
endif
|
||||
|
||||
BINARIES:=iso9660fs
|
||||
|
||||
all: $(BINARIES)
|
||||
|
||||
.PHONY: all install clean
|
||||
|
||||
install: all
|
||||
mkdir -p $(DESTDIR)$(SBINDIR)
|
||||
install $(BINARIES) $(DESTDIR)$(SBINDIR)
|
||||
|
||||
iso9660fs: *.cpp *.h
|
||||
$(CXX) -std=gnu++11 $(CPPFLAGS) $(CXXFLAGS) *.cpp -o $@ $(LIBS)
|
||||
|
||||
clean:
|
||||
rm -f $(BINARIES) *.o
|
108
iso9660/block.cpp
Normal file
108
iso9660/block.cpp
Normal file
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* block.cpp
|
||||
* Blocks in the filesystem.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "block.h"
|
||||
#include "device.h"
|
||||
#include "ioleast.h"
|
||||
|
||||
Block::Block()
|
||||
{
|
||||
this->block_data = NULL;
|
||||
}
|
||||
|
||||
Block::Block(Device* device, uint32_t block_id)
|
||||
{
|
||||
Construct(device, block_id);
|
||||
}
|
||||
|
||||
void Block::Construct(Device* device, uint32_t block_id)
|
||||
{
|
||||
this->prev_block = NULL;
|
||||
this->next_block = NULL;
|
||||
this->prev_hashed = NULL;
|
||||
this->next_hashed = NULL;
|
||||
this->device = device;
|
||||
this->reference_count = 1;
|
||||
this->block_id = block_id;
|
||||
}
|
||||
|
||||
Block::~Block()
|
||||
{
|
||||
Destruct();
|
||||
delete[] block_data;
|
||||
}
|
||||
|
||||
void Block::Destruct()
|
||||
{
|
||||
Unlink();
|
||||
}
|
||||
|
||||
void Block::Refer()
|
||||
{
|
||||
reference_count++;
|
||||
}
|
||||
|
||||
void Block::Unref()
|
||||
{
|
||||
if ( !--reference_count )
|
||||
{
|
||||
#if 0
|
||||
device->block_count--;
|
||||
delete this;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Block::Use()
|
||||
{
|
||||
Unlink();
|
||||
Prelink();
|
||||
}
|
||||
|
||||
void Block::Unlink()
|
||||
{
|
||||
(prev_block ? prev_block->next_block : device->mru_block) = next_block;
|
||||
(next_block ? next_block->prev_block : device->lru_block) = prev_block;
|
||||
size_t bin = block_id % DEVICE_HASH_LENGTH;
|
||||
(prev_hashed ? prev_hashed->next_hashed : device->hash_blocks[bin]) = next_hashed;
|
||||
if ( next_hashed ) next_hashed->prev_hashed = prev_hashed;
|
||||
}
|
||||
|
||||
void Block::Prelink()
|
||||
{
|
||||
prev_block = NULL;
|
||||
next_block = device->mru_block;
|
||||
if ( device->mru_block )
|
||||
device->mru_block->prev_block = this;
|
||||
device->mru_block = this;
|
||||
if ( !device->lru_block )
|
||||
device->lru_block = this;
|
||||
size_t bin = block_id % DEVICE_HASH_LENGTH;
|
||||
prev_hashed = NULL;
|
||||
next_hashed = device->hash_blocks[bin];
|
||||
device->hash_blocks[bin] = this;
|
||||
if ( next_hashed )
|
||||
next_hashed->prev_hashed = this;
|
||||
}
|
53
iso9660/block.h
Normal file
53
iso9660/block.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* block.h
|
||||
* Blocks in the filesystem.
|
||||
*/
|
||||
|
||||
#ifndef BLOCK_H
|
||||
#define BLOCK_H
|
||||
|
||||
class Device;
|
||||
|
||||
class Block
|
||||
{
|
||||
public:
|
||||
Block();
|
||||
Block(Device* device, uint32_t block_id);
|
||||
~Block();
|
||||
void Construct(Device* device, uint32_t block_id);
|
||||
void Destruct();
|
||||
|
||||
public:
|
||||
Block* prev_block;
|
||||
Block* next_block;
|
||||
Block* prev_hashed;
|
||||
Block* next_hashed;
|
||||
Device* device;
|
||||
size_t reference_count;
|
||||
uint32_t block_id;
|
||||
uint8_t* block_data;
|
||||
|
||||
public:
|
||||
void Refer();
|
||||
void Unref();
|
||||
void Use();
|
||||
void Unlink();
|
||||
void Prelink();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
107
iso9660/device.cpp
Normal file
107
iso9660/device.cpp
Normal file
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* device.cpp
|
||||
* Block device.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "block.h"
|
||||
#include "device.h"
|
||||
#include "ioleast.h"
|
||||
|
||||
Device::Device(int fd, const char* path, uint32_t block_size)
|
||||
{
|
||||
this->mru_block = NULL;
|
||||
this->lru_block = NULL;
|
||||
for ( size_t i = 0; i < DEVICE_HASH_LENGTH; i++ )
|
||||
hash_blocks[i] = NULL;
|
||||
struct stat st;
|
||||
fstat(fd, &st);
|
||||
this->device_size = st.st_size;
|
||||
this->path = path;
|
||||
this->block_size = block_size;
|
||||
this->fd = fd;
|
||||
this->block_count = 0;
|
||||
#ifdef __sortix__
|
||||
// TODO: This isn't scaleable if there's multiple filesystems mounted.
|
||||
size_t memory;
|
||||
memstat(NULL, &memory);
|
||||
this->block_limit = (memory / 10) / block_size;
|
||||
#else
|
||||
this->block_limit = 32768;
|
||||
#endif
|
||||
}
|
||||
|
||||
Device::~Device()
|
||||
{
|
||||
while ( mru_block )
|
||||
delete mru_block;
|
||||
close(fd);
|
||||
}
|
||||
|
||||
Block* Device::AllocateBlock()
|
||||
{
|
||||
if ( block_limit <= block_count )
|
||||
{
|
||||
for ( Block* block = lru_block; block; block = block->prev_block )
|
||||
{
|
||||
if ( block->reference_count )
|
||||
continue;
|
||||
block->Destruct(); // Syncs.
|
||||
return block;
|
||||
}
|
||||
}
|
||||
uint8_t* data = new uint8_t[block_size];
|
||||
if ( !data ) // TODO: Use operator new nothrow!
|
||||
return NULL;
|
||||
Block* block = new Block();
|
||||
if ( !block ) // TODO: Use operator new nothrow!
|
||||
return delete[] data, (Block*) NULL;
|
||||
block->block_data = data;
|
||||
block_count++;
|
||||
return block;
|
||||
}
|
||||
|
||||
Block* Device::GetBlock(uint32_t block_id)
|
||||
{
|
||||
if ( Block* block = GetCachedBlock(block_id) )
|
||||
return block;
|
||||
Block* block = AllocateBlock();
|
||||
if ( !block )
|
||||
return NULL;
|
||||
block->Construct(this, block_id);
|
||||
off_t file_offset = (off_t) block_size * (off_t) block_id;
|
||||
preadall(fd, block->block_data, block_size, file_offset);
|
||||
block->Prelink();
|
||||
return block;
|
||||
}
|
||||
|
||||
Block* Device::GetCachedBlock(uint32_t block_id)
|
||||
{
|
||||
size_t bin = block_id % DEVICE_HASH_LENGTH;
|
||||
for ( Block* iter = hash_blocks[bin]; iter; iter = iter->next_hashed )
|
||||
if ( iter->block_id == block_id )
|
||||
return iter->Refer(), iter;
|
||||
return NULL;
|
||||
}
|
51
iso9660/device.h
Normal file
51
iso9660/device.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* device.h
|
||||
* Block device.
|
||||
*/
|
||||
|
||||
#ifndef DEVICE_H
|
||||
#define DEVICE_H
|
||||
|
||||
class Block;
|
||||
|
||||
static const size_t DEVICE_HASH_LENGTH = 1 << 16;
|
||||
|
||||
class Device
|
||||
{
|
||||
public:
|
||||
Device(int fd, const char* path, uint32_t block_size);
|
||||
~Device();
|
||||
|
||||
public:
|
||||
Block* mru_block;
|
||||
Block* lru_block;
|
||||
Block* hash_blocks[DEVICE_HASH_LENGTH];
|
||||
off_t device_size;
|
||||
const char* path;
|
||||
uint32_t block_size;
|
||||
int fd;
|
||||
size_t block_count;
|
||||
size_t block_limit;
|
||||
|
||||
public:
|
||||
Block* AllocateBlock();
|
||||
Block* GetBlock(uint32_t block_id);
|
||||
Block* GetCachedBlock(uint32_t block_id);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
87
iso9660/filesystem.cpp
Normal file
87
iso9660/filesystem.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* filesystem.cpp
|
||||
* ISO 9660 filesystem implementation.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <stdio.h> // DEBUG
|
||||
|
||||
#include "block.h"
|
||||
#include "device.h"
|
||||
#include "filesystem.h"
|
||||
#include "inode.h"
|
||||
#include "util.h"
|
||||
|
||||
Filesystem::Filesystem(Device* device, const char* mount_path,
|
||||
uint64_t pvd_offset)
|
||||
{
|
||||
// TODO: This should be replaced by the . entry within the root directory
|
||||
// so the Rock Ridge extensions are available.
|
||||
uint32_t pvd_block_id = pvd_offset / device->block_size;
|
||||
this->pvd_block = device->GetBlock(pvd_block_id);
|
||||
assert(pvd_block); // TODO: This can fail.
|
||||
this->pvd = (struct iso9660_pvd*)
|
||||
(pvd_block->block_data + pvd_offset % device->block_size);
|
||||
this->root_ino = pvd_offset + offsetof(struct iso9660_pvd, root_dirent);
|
||||
this->device = device;
|
||||
this->mount_path = mount_path;
|
||||
this->block_size = device->block_size;
|
||||
this->mru_inode = NULL;
|
||||
this->lru_inode = NULL;
|
||||
for ( size_t i = 0; i < INODE_HASH_LENGTH; i++ )
|
||||
this->hash_inodes[i] = NULL;
|
||||
}
|
||||
|
||||
Filesystem::~Filesystem()
|
||||
{
|
||||
while ( mru_inode )
|
||||
delete mru_inode;
|
||||
pvd_block->Unref();
|
||||
}
|
||||
|
||||
Inode* Filesystem::GetInode(iso9660_ino_t inode_id)
|
||||
{
|
||||
size_t bin = inode_id % INODE_HASH_LENGTH;
|
||||
for ( Inode* iter = hash_inodes[bin]; iter; iter = iter->next_hashed )
|
||||
if ( iter->inode_id == inode_id )
|
||||
return iter->Refer(), iter;
|
||||
|
||||
uint32_t block_id = inode_id / block_size;
|
||||
uint32_t offset = inode_id % block_size;
|
||||
|
||||
Block* block = device->GetBlock(block_id);
|
||||
if ( !block )
|
||||
return (Inode*) NULL;
|
||||
Inode* inode = new Inode(this, inode_id);
|
||||
if ( !inode )
|
||||
return block->Unref(), (Inode*) NULL;
|
||||
inode->data_block = block;
|
||||
uint8_t* buf = inode->data_block->block_data + offset;
|
||||
inode->data = (struct iso9660_dirent*) buf;
|
||||
inode->Prelink();
|
||||
inode->Parse();
|
||||
|
||||
return inode;
|
||||
}
|
52
iso9660/filesystem.h
Normal file
52
iso9660/filesystem.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* filesystem.h
|
||||
* ISO 9660 filesystem implementation.
|
||||
*/
|
||||
|
||||
#ifndef FILESYSTEM_H
|
||||
#define FILESYSTEM_H
|
||||
|
||||
#include "iso9660.h"
|
||||
|
||||
class Device;
|
||||
class Inode;
|
||||
|
||||
static const size_t INODE_HASH_LENGTH = 1 << 16;
|
||||
|
||||
class Filesystem
|
||||
{
|
||||
public:
|
||||
Filesystem(Device* device, const char* mount_path, uint64_t pvd_offset);
|
||||
~Filesystem();
|
||||
|
||||
public:
|
||||
Block* pvd_block;
|
||||
struct iso9660_pvd* pvd;
|
||||
Device* device;
|
||||
const char* mount_path;
|
||||
iso9660_ino_t root_ino;
|
||||
uint32_t block_size;
|
||||
Inode* mru_inode;
|
||||
Inode* lru_inode;
|
||||
Inode* hash_inodes[INODE_HASH_LENGTH];
|
||||
|
||||
public:
|
||||
Inode* GetInode(iso9660_ino_t inode_id);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
701
iso9660/fsmarshall.cpp
Normal file
701
iso9660/fsmarshall.cpp
Normal file
|
@ -0,0 +1,701 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2014, 2015, 2016, 2022, 2023 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* fsmarshall.cpp
|
||||
* Sortix fsmarshall frontend.
|
||||
*/
|
||||
|
||||
#if defined(__sortix__)
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <ioleast.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <timespec.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sortix/dirent.h>
|
||||
|
||||
#include <fsmarshall.h>
|
||||
|
||||
#include "block.h"
|
||||
#include "device.h"
|
||||
#include "filesystem.h"
|
||||
#include "fsmarshall.h"
|
||||
#include "fuse.h"
|
||||
#include "inode.h"
|
||||
#include "iso9660fs.h"
|
||||
|
||||
bool RespondData(int chl, const void* ptr, size_t count)
|
||||
{
|
||||
return writeall(chl, ptr, count) == count;
|
||||
}
|
||||
|
||||
bool RespondHeader(int chl, size_t type, size_t size)
|
||||
{
|
||||
struct fsm_msg_header hdr;
|
||||
hdr.msgtype = type;
|
||||
hdr.msgsize = size;
|
||||
return RespondData(chl, &hdr, sizeof(hdr));
|
||||
}
|
||||
|
||||
bool RespondMessage(int chl, size_t type, const void* ptr, size_t count)
|
||||
{
|
||||
return RespondHeader(chl, type, count) &&
|
||||
RespondData(chl, ptr, count);
|
||||
}
|
||||
|
||||
bool RespondError(int chl, int errnum)
|
||||
{
|
||||
struct fsm_resp_error body;
|
||||
body.errnum = errnum;
|
||||
return RespondMessage(chl, FSM_RESP_ERROR, &body, sizeof(body));
|
||||
}
|
||||
|
||||
bool RespondSuccess(int chl)
|
||||
{
|
||||
struct fsm_resp_success body;
|
||||
return RespondMessage(chl, FSM_RESP_SUCCESS, &body, sizeof(body));
|
||||
}
|
||||
|
||||
bool RespondStat(int chl, struct stat* st)
|
||||
{
|
||||
struct fsm_resp_stat body;
|
||||
body.st = *st;
|
||||
return RespondMessage(chl, FSM_RESP_STAT, &body, sizeof(body));
|
||||
}
|
||||
|
||||
bool RespondStatVFS(int chl, struct statvfs* stvfs)
|
||||
{
|
||||
struct fsm_resp_statvfs body;
|
||||
body.stvfs = *stvfs;
|
||||
return RespondMessage(chl, FSM_RESP_STATVFS, &body, sizeof(body));
|
||||
}
|
||||
|
||||
bool RespondSeek(int chl, off_t offset)
|
||||
{
|
||||
struct fsm_resp_lseek body;
|
||||
body.offset = offset;
|
||||
return RespondMessage(chl, FSM_RESP_LSEEK, &body, sizeof(body));
|
||||
}
|
||||
|
||||
bool RespondRead(int chl, const uint8_t* buf, size_t count)
|
||||
{
|
||||
struct fsm_resp_read body;
|
||||
body.count = count;
|
||||
return RespondMessage(chl, FSM_RESP_READ, &body, sizeof(body)) &&
|
||||
RespondData(chl, buf, count);
|
||||
}
|
||||
|
||||
bool RespondReadlink(int chl, const uint8_t* buf, size_t count)
|
||||
{
|
||||
struct fsm_resp_readlink body;
|
||||
body.targetlen = count;
|
||||
return RespondMessage(chl, FSM_RESP_READLINK, &body, sizeof(body)) &&
|
||||
RespondData(chl, buf, count);
|
||||
}
|
||||
|
||||
bool RespondWrite(int chl, size_t count)
|
||||
{
|
||||
struct fsm_resp_write body;
|
||||
body.count = count;
|
||||
return RespondMessage(chl, FSM_RESP_WRITE, &body, sizeof(body));
|
||||
}
|
||||
|
||||
bool RespondOpen(int chl, ino_t ino, mode_t type)
|
||||
{
|
||||
struct fsm_resp_open body;
|
||||
body.ino = ino;
|
||||
body.type = type;
|
||||
return RespondMessage(chl, FSM_RESP_OPEN, &body, sizeof(body));
|
||||
}
|
||||
|
||||
bool RespondMakeDir(int chl, ino_t ino)
|
||||
{
|
||||
struct fsm_resp_mkdir body;
|
||||
body.ino = ino;
|
||||
return RespondMessage(chl, FSM_RESP_MKDIR, &body, sizeof(body));
|
||||
}
|
||||
|
||||
bool RespondReadDir(int chl, struct dirent* dirent)
|
||||
{
|
||||
struct fsm_resp_readdirents body;
|
||||
body.ino = dirent->d_ino;
|
||||
body.type = dirent->d_type;
|
||||
body.namelen = dirent->d_namlen;
|
||||
return RespondMessage(chl, FSM_RESP_READDIRENTS, &body, sizeof(body)) &&
|
||||
RespondData(chl, dirent->d_name, dirent->d_namlen);
|
||||
}
|
||||
|
||||
bool RespondTCGetBlob(int chl, const void* data, size_t data_size)
|
||||
{
|
||||
struct fsm_resp_tcgetblob body;
|
||||
body.count = data_size;
|
||||
return RespondMessage(chl, FSM_RESP_TCGETBLOB, &body, sizeof(body)) &&
|
||||
RespondData(chl, data, data_size);
|
||||
}
|
||||
|
||||
Inode* SafeGetInode(Filesystem* fs, ino_t ino)
|
||||
{
|
||||
if ( (iso9660_ino_t) ino != ino )
|
||||
return errno = EBADF, (Inode*) ino;
|
||||
return fs->GetInode((iso9660_ino_t) ino);
|
||||
}
|
||||
|
||||
void HandleRefer(int chl, struct fsm_req_refer* msg, Filesystem* fs)
|
||||
{
|
||||
(void) chl;
|
||||
if ( Inode* inode = SafeGetInode(fs, msg->ino) )
|
||||
{
|
||||
inode->RemoteRefer();
|
||||
inode->Unref();
|
||||
}
|
||||
}
|
||||
|
||||
void HandleUnref(int chl, struct fsm_req_unref* msg, Filesystem* fs)
|
||||
{
|
||||
(void) chl;
|
||||
if ( Inode* inode = SafeGetInode(fs, msg->ino) )
|
||||
{
|
||||
inode->RemoteUnref();
|
||||
inode->Unref();
|
||||
}
|
||||
}
|
||||
|
||||
void HandleSync(int chl, struct fsm_req_sync* msg, Filesystem* fs)
|
||||
{
|
||||
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||
if ( !inode ) { RespondError(chl, errno); return; }
|
||||
inode->Unref();
|
||||
RespondSuccess(chl);
|
||||
}
|
||||
|
||||
void HandleStat(int chl, struct fsm_req_stat* msg, Filesystem* fs)
|
||||
{
|
||||
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||
if ( !inode ) { RespondError(chl, errno); return; }
|
||||
struct stat st;
|
||||
StatInode(inode, &st);
|
||||
inode->Unref();
|
||||
RespondStat(chl, &st);
|
||||
}
|
||||
|
||||
void HandleChangeMode(int chl, struct fsm_req_chmod* /*msg*/, Filesystem* /*fs*/)
|
||||
{
|
||||
RespondError(chl, EROFS);
|
||||
}
|
||||
|
||||
void HandleChangeOwner(int chl, struct fsm_req_chown* /*msg*/, Filesystem* /*fs*/)
|
||||
{
|
||||
RespondError(chl, EROFS);
|
||||
}
|
||||
|
||||
void HandleUTimens(int chl, struct fsm_req_utimens* /*msg*/, Filesystem* /*fs*/)
|
||||
{
|
||||
RespondError(chl, EROFS);
|
||||
}
|
||||
|
||||
void HandleTruncate(int chl, struct fsm_req_truncate* /*msg*/, Filesystem* /*fs*/)
|
||||
{
|
||||
RespondError(chl, EROFS);
|
||||
}
|
||||
|
||||
void HandleSeek(int chl, struct fsm_req_lseek* msg, Filesystem* fs)
|
||||
{
|
||||
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||
if ( !inode ) { RespondError(chl, errno); return; }
|
||||
if ( msg->whence == SEEK_SET )
|
||||
RespondSeek(chl, msg->offset);
|
||||
else if ( msg->whence == SEEK_END )
|
||||
{
|
||||
off_t inode_size = inode->Size();
|
||||
if ( (msg->offset < 0 && inode_size + msg->offset < 0) ||
|
||||
(0 <= msg->offset && OFF_MAX - inode_size < msg->offset) )
|
||||
RespondError(chl, EOVERFLOW);
|
||||
else
|
||||
RespondSeek(chl, msg->offset + inode_size);
|
||||
}
|
||||
else
|
||||
RespondError(chl, EINVAL);
|
||||
inode->Unref();
|
||||
}
|
||||
|
||||
void HandleReadAt(int chl, struct fsm_req_pread* msg, Filesystem* fs)
|
||||
{
|
||||
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||
if ( !inode ) { RespondError(chl, errno); return; }
|
||||
uint8_t* buf = (uint8_t*) malloc(msg->count);
|
||||
if ( !buf ) { inode->Unref(); RespondError(chl, errno); return; }
|
||||
ssize_t amount = inode->ReadAt(buf, msg->count, msg->offset);
|
||||
inode->Unref();
|
||||
if ( amount < 0 ) { free(buf); RespondError(chl, errno); return; }
|
||||
RespondRead(chl, buf, amount);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void HandleWriteAt(int chl, struct fsm_req_pwrite* /*msg*/, Filesystem* /*fs*/)
|
||||
{
|
||||
RespondError(chl, EROFS);
|
||||
}
|
||||
|
||||
void HandleOpen(int chl, struct fsm_req_open* msg, Filesystem* fs)
|
||||
{
|
||||
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||
if ( !inode ) { RespondError(chl, errno); return; }
|
||||
|
||||
char* pathraw = (char*) &(msg[1]);
|
||||
char* path = (char*) malloc(msg->namelen+1);
|
||||
if ( !path )
|
||||
{
|
||||
RespondError(chl, errno);
|
||||
inode->Unref();
|
||||
return;
|
||||
}
|
||||
memcpy(path, pathraw, msg->namelen);
|
||||
path[msg->namelen] = '\0';
|
||||
|
||||
Inode* result = inode->Open(path, msg->flags, FsModeFromHostMode(msg->mode));
|
||||
|
||||
free(path);
|
||||
inode->Unref();
|
||||
|
||||
if ( !result ) { RespondError(chl, errno); return; }
|
||||
|
||||
RespondOpen(chl, result->inode_id, result->Mode() & S_IFMT);
|
||||
result->Unref();
|
||||
}
|
||||
|
||||
void HandleMakeDir(int chl, struct fsm_req_mkdir* msg, Filesystem* fs)
|
||||
{
|
||||
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||
if ( !inode ) { RespondError(chl, errno); return; }
|
||||
inode->Unref();
|
||||
RespondError(chl, EROFS);
|
||||
}
|
||||
|
||||
void HandleReadDir(int chl, struct fsm_req_readdirents* msg, Filesystem* fs)
|
||||
{
|
||||
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||
if ( !inode ) { RespondError(chl, errno); return; }
|
||||
if ( !S_ISDIR(inode->Mode()) )
|
||||
{
|
||||
inode->Unref();
|
||||
RespondError(chl, ENOTDIR);
|
||||
return;
|
||||
}
|
||||
union
|
||||
{
|
||||
struct dirent kernel_entry;
|
||||
uint8_t padding[sizeof(struct dirent) + 256];
|
||||
};
|
||||
memset(&kernel_entry, 0, sizeof(kernel_entry));
|
||||
|
||||
uint64_t offset = 0;
|
||||
Block* block = NULL;
|
||||
uint64_t block_id = 0;
|
||||
char name[256];
|
||||
uint8_t file_type;
|
||||
iso9660_ino_t inode_id;
|
||||
while ( inode->ReadDirectory(&offset, &block, &block_id,
|
||||
msg->rec_num ? NULL : name, &file_type,
|
||||
&inode_id) )
|
||||
{
|
||||
if ( !(msg->rec_num--) )
|
||||
{
|
||||
size_t name_len = strlen(name);
|
||||
kernel_entry.d_reclen = sizeof(kernel_entry) + name_len;
|
||||
kernel_entry.d_ino = inode_id;
|
||||
kernel_entry.d_dev = 0;
|
||||
kernel_entry.d_type = HostDTFromFsDT(file_type);
|
||||
kernel_entry.d_namlen = name_len;
|
||||
memcpy(kernel_entry.d_name, name, name_len);
|
||||
size_t dname_offset = offsetof(struct dirent, d_name);
|
||||
padding[dname_offset + kernel_entry.d_namlen] = '\0';
|
||||
block->Unref();
|
||||
inode->Unref();
|
||||
RespondReadDir(chl, &kernel_entry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
int errnum = errno;
|
||||
if ( block )
|
||||
block->Unref();
|
||||
inode->Unref();
|
||||
|
||||
if ( errnum )
|
||||
{
|
||||
RespondError(chl, errnum);
|
||||
return;
|
||||
}
|
||||
|
||||
kernel_entry.d_reclen = sizeof(kernel_entry);
|
||||
RespondReadDir(chl, &kernel_entry);
|
||||
}
|
||||
|
||||
void HandleIsATTY(int chl, struct fsm_req_isatty* msg, Filesystem* fs)
|
||||
{
|
||||
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||
if ( !inode ) { RespondError(chl, errno); return; }
|
||||
RespondError(chl, ENOTTY);
|
||||
inode->Unref();
|
||||
}
|
||||
|
||||
void HandleUnlink(int chl, struct fsm_req_unlink* msg, Filesystem* fs)
|
||||
{
|
||||
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||
if ( !inode ) { RespondError(chl, errno); return; }
|
||||
|
||||
char* pathraw = (char*) &(msg[1]);
|
||||
char* path = (char*) malloc(msg->namelen+1);
|
||||
if ( !path )
|
||||
{
|
||||
RespondError(chl, errno);
|
||||
inode->Unref();
|
||||
return;
|
||||
}
|
||||
memcpy(path, pathraw, msg->namelen);
|
||||
path[msg->namelen] = '\0';
|
||||
|
||||
bool result = inode->Unlink(path, false);
|
||||
free(path);
|
||||
inode->Unref();
|
||||
|
||||
if ( !result ) { RespondError(chl, errno); return; }
|
||||
|
||||
RespondSuccess(chl);
|
||||
}
|
||||
|
||||
void HandleRemoveDir(int chl, struct fsm_req_rmdir* msg, Filesystem* fs)
|
||||
{
|
||||
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||
if ( !inode ) { RespondError(chl, errno); return; }
|
||||
|
||||
char* pathraw = (char*) &(msg[1]);
|
||||
char* path = (char*) malloc(msg->namelen+1);
|
||||
if ( !path )
|
||||
{
|
||||
RespondError(chl, errno);
|
||||
inode->Unref();
|
||||
return;
|
||||
}
|
||||
memcpy(path, pathraw, msg->namelen);
|
||||
path[msg->namelen] = '\0';
|
||||
|
||||
bool result = inode->RemoveDirectory(path);
|
||||
free(path);
|
||||
inode->Unref();
|
||||
|
||||
if ( !result ) { RespondError(chl, errno); return; }
|
||||
|
||||
RespondSuccess(chl);
|
||||
}
|
||||
|
||||
void HandleLink(int chl, struct fsm_req_link* msg, Filesystem* fs)
|
||||
{
|
||||
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||
if ( !inode ) { RespondError(chl, errno); return; }
|
||||
Inode* dest = SafeGetInode(fs, msg->linkino);
|
||||
if ( !dest ) { inode->Unref(); RespondError(chl, errno); return; }
|
||||
|
||||
char* pathraw = (char*) &(msg[1]);
|
||||
char* path = (char*) malloc(msg->namelen+1);
|
||||
if ( !path )
|
||||
{
|
||||
RespondError(chl, errno);
|
||||
inode->Unref();
|
||||
return;
|
||||
}
|
||||
memcpy(path, pathraw, msg->namelen);
|
||||
path[msg->namelen] = '\0';
|
||||
|
||||
bool result = inode->Link(path, dest);
|
||||
|
||||
free(path);
|
||||
dest->Unref();
|
||||
inode->Unref();
|
||||
|
||||
if ( !result ) { RespondError(chl, errno); return; }
|
||||
|
||||
RespondSuccess(chl);
|
||||
}
|
||||
|
||||
void HandleSymlink(int chl, struct fsm_req_symlink* msg, Filesystem* fs)
|
||||
{
|
||||
Inode* inode = SafeGetInode(fs, msg->dirino);
|
||||
if ( !inode ) { RespondError(chl, errno); return; }
|
||||
inode->Unref();
|
||||
RespondError(chl, EROFS);
|
||||
}
|
||||
|
||||
void HandleReadlink(int chl, struct fsm_req_readlink* msg, Filesystem* fs)
|
||||
{
|
||||
Inode* inode = SafeGetInode(fs, msg->ino);
|
||||
if ( !inode ) { RespondError(chl, errno); return; }
|
||||
if ( !ISO9660_S_ISLNK(inode->Mode()) ) { inode->Unref(); RespondError(chl, EINVAL); return; }
|
||||
size_t count = inode->Size();
|
||||
uint8_t* buf = (uint8_t*) malloc(count);
|
||||
if ( !buf ) { inode->Unref(); RespondError(chl, errno); return; }
|
||||
ssize_t amount = inode->ReadLink(buf, count);
|
||||
inode->Unref();
|
||||
if ( amount < 0 ) { RespondError(chl, errno); return; }
|
||||
RespondReadlink(chl, buf, amount);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void HandleRename(int chl, struct fsm_req_rename* msg, Filesystem* fs)
|
||||
{
|
||||
char* pathraw = (char*) &(msg[1]);
|
||||
char* path = (char*) malloc(msg->oldnamelen+1 + msg->newnamelen+1);
|
||||
if ( !path ) { RespondError(chl, errno); return; }
|
||||
memcpy(path, pathraw, msg->oldnamelen);
|
||||
path[msg->oldnamelen] = '\0';
|
||||
memcpy(path + msg->oldnamelen + 1, pathraw + msg->oldnamelen, msg->newnamelen);
|
||||
path[msg->oldnamelen + 1 + msg->newnamelen] = '\0';
|
||||
|
||||
const char* oldname = path;
|
||||
const char* newname = path + msg->oldnamelen + 1;
|
||||
|
||||
Inode* olddir = SafeGetInode(fs, msg->olddirino);
|
||||
if ( !olddir ) { free(path); RespondError(chl, errno); return; }
|
||||
Inode* newdir = SafeGetInode(fs, msg->newdirino);
|
||||
if ( !newdir ) { olddir->Unref(); free(path); RespondError(chl, errno); return; }
|
||||
|
||||
bool result = newdir->Rename(olddir, oldname, newname);
|
||||
|
||||
newdir->Unref();
|
||||
olddir->Unref();
|
||||
free(path);
|
||||
|
||||
if ( !result ) { RespondError(chl, errno); return; }
|
||||
|
||||
RespondSuccess(chl);
|
||||
}
|
||||
|
||||
void HandleStatVFS(int chl, struct fsm_req_statvfs* msg, Filesystem* fs)
|
||||
{
|
||||
(void) msg;
|
||||
struct statvfs stvfs;
|
||||
stvfs.f_bsize = fs->block_size;
|
||||
stvfs.f_frsize = fs->block_size;
|
||||
stvfs.f_blocks = fs->device->device_size / fs->block_size;
|
||||
stvfs.f_bfree = 0;
|
||||
stvfs.f_bavail = 0;
|
||||
stvfs.f_files = 0;
|
||||
stvfs.f_ffree = 0;
|
||||
stvfs.f_favail = 0;
|
||||
stvfs.f_ffree = 0;
|
||||
stvfs.f_fsid = 0;
|
||||
stvfs.f_flag = ST_RDONLY;
|
||||
stvfs.f_namemax = 255;
|
||||
RespondStatVFS(chl, &stvfs);
|
||||
}
|
||||
|
||||
void HandleTCGetBlob(int chl, struct fsm_req_tcgetblob* msg, Filesystem* fs)
|
||||
{
|
||||
char* nameraw = (char*) &(msg[1]);
|
||||
char* name = (char*) malloc(msg->namelen + 1);
|
||||
if ( !name )
|
||||
return (void) RespondError(chl, errno);
|
||||
memcpy(name, nameraw, msg->namelen);
|
||||
name[msg->namelen] = '\0';
|
||||
|
||||
//static const char index[] = "device-path\0filesystem-type\0filesystem-uuid\0mount-path\0";
|
||||
static const char index[] = "device-path\0filesystem-type\0mount-path\0";
|
||||
if ( !strcmp(name, "") )
|
||||
RespondTCGetBlob(chl, index, sizeof(index) - 1);
|
||||
else if ( !strcmp(name, "device-path") )
|
||||
RespondTCGetBlob(chl, fs->device->path, strlen(fs->device->path));
|
||||
else if ( !strcmp(name, "filesystem-type") )
|
||||
RespondTCGetBlob(chl, "iso9660", strlen("iso9660"));
|
||||
// TODO: Some kind of unique id.
|
||||
//else if ( !strcmp(name, "filesystem-uuid") )
|
||||
// RespondTCGetBlob(chl, fs->sb->s_uuid, sizeof(fs->sb->s_uuid));
|
||||
else if ( !strcmp(name, "mount-path") )
|
||||
RespondTCGetBlob(chl, fs->mount_path, strlen(fs->mount_path));
|
||||
else
|
||||
RespondError(chl, ENOENT);
|
||||
|
||||
free(name);
|
||||
}
|
||||
|
||||
void HandleIncomingMessage(int chl, struct fsm_msg_header* hdr, Filesystem* fs)
|
||||
{
|
||||
request_uid = hdr->uid;
|
||||
request_gid = hdr->gid;
|
||||
if ( (uint16_t) request_uid != request_uid ||
|
||||
(uint16_t) request_gid != request_gid )
|
||||
{
|
||||
fprintf(stderr, "extfs: id exceeded 16-bit: uid=%ju gid=%ju\n",
|
||||
(uintmax_t) request_uid, (uintmax_t) request_gid);
|
||||
RespondError(chl, EOVERFLOW);
|
||||
return;
|
||||
}
|
||||
typedef void (*handler_t)(int, void*, Filesystem*);
|
||||
handler_t handlers[FSM_MSG_NUM] = { NULL };
|
||||
handlers[FSM_REQ_SYNC] = (handler_t) HandleSync;
|
||||
handlers[FSM_REQ_STAT] = (handler_t) HandleStat;
|
||||
handlers[FSM_REQ_CHMOD] = (handler_t) HandleChangeMode;
|
||||
handlers[FSM_REQ_CHOWN] = (handler_t) HandleChangeOwner;
|
||||
handlers[FSM_REQ_TRUNCATE] = (handler_t) HandleTruncate;
|
||||
handlers[FSM_REQ_LSEEK] = (handler_t) HandleSeek;
|
||||
handlers[FSM_REQ_PREAD] = (handler_t) HandleReadAt;
|
||||
handlers[FSM_REQ_OPEN] = (handler_t) HandleOpen;
|
||||
handlers[FSM_REQ_READDIRENTS] = (handler_t) HandleReadDir;
|
||||
handlers[FSM_REQ_PWRITE] = (handler_t) HandleWriteAt;
|
||||
handlers[FSM_REQ_ISATTY] = (handler_t) HandleIsATTY;
|
||||
handlers[FSM_REQ_UTIMENS] = (handler_t) HandleUTimens;
|
||||
handlers[FSM_REQ_MKDIR] = (handler_t) HandleMakeDir;
|
||||
handlers[FSM_REQ_RMDIR] = (handler_t) HandleRemoveDir;
|
||||
handlers[FSM_REQ_UNLINK] = (handler_t) HandleUnlink;
|
||||
handlers[FSM_REQ_LINK] = (handler_t) HandleLink;
|
||||
handlers[FSM_REQ_SYMLINK] = (handler_t) HandleSymlink;
|
||||
handlers[FSM_REQ_READLINK] = (handler_t) HandleReadlink;
|
||||
handlers[FSM_REQ_RENAME] = (handler_t) HandleRename;
|
||||
handlers[FSM_REQ_REFER] = (handler_t) HandleRefer;
|
||||
handlers[FSM_REQ_UNREF] = (handler_t) HandleUnref;
|
||||
handlers[FSM_REQ_STATVFS] = (handler_t) HandleStatVFS;
|
||||
handlers[FSM_REQ_TCGETBLOB] = (handler_t) HandleTCGetBlob;
|
||||
if ( FSM_MSG_NUM <= hdr->msgtype || !handlers[hdr->msgtype] )
|
||||
{
|
||||
fprintf(stderr, "extfs: message type %zu not supported\n", hdr->msgtype);
|
||||
RespondError(chl, ENOTSUP);
|
||||
return;
|
||||
}
|
||||
uint8_t body_buffer[65536];
|
||||
uint8_t* body = body_buffer;
|
||||
if ( sizeof(body_buffer) < hdr->msgsize )
|
||||
{
|
||||
body = (uint8_t*) mmap(NULL, hdr->msgsize, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if ( (void*) body == MAP_FAILED )
|
||||
{
|
||||
RespondError(chl, errno);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ( readall(chl, body, hdr->msgsize) == hdr->msgsize )
|
||||
handlers[hdr->msgtype](chl, body, fs);
|
||||
else
|
||||
RespondError(chl, errno);
|
||||
if ( sizeof(body_buffer) < hdr->msgsize )
|
||||
munmap(body, hdr->msgsize);
|
||||
}
|
||||
static volatile bool should_terminate = false;
|
||||
|
||||
void TerminationHandler(int)
|
||||
{
|
||||
should_terminate = true;
|
||||
}
|
||||
|
||||
static void ready(void)
|
||||
{
|
||||
const char* readyfd_env = getenv("READYFD");
|
||||
if ( !readyfd_env )
|
||||
return;
|
||||
int readyfd = atoi(readyfd_env);
|
||||
char c = '\n';
|
||||
write(readyfd, &c, 1);
|
||||
close(readyfd);
|
||||
unsetenv("READYFD");
|
||||
}
|
||||
|
||||
int fsmarshall_main(const char* argv0,
|
||||
const char* mount_path,
|
||||
bool foreground,
|
||||
Filesystem* fs,
|
||||
Device* dev)
|
||||
{
|
||||
(void) argv0;
|
||||
|
||||
// Stat the root inode.
|
||||
struct stat root_inode_st;
|
||||
Inode* root_inode = fs->GetInode(fs->root_ino);
|
||||
if ( !root_inode )
|
||||
err(1, "GetInode");
|
||||
StatInode(root_inode, &root_inode_st);
|
||||
root_inode->Unref();
|
||||
|
||||
// Create a filesystem server connected to the kernel that we'll listen on.
|
||||
int serverfd = fsm_mountat(AT_FDCWD, mount_path, &root_inode_st, 0);
|
||||
if ( serverfd < 0 )
|
||||
err(1, "%s", mount_path);
|
||||
|
||||
// Make sure the server isn't unexpectedly killed and data is lost.
|
||||
signal(SIGINT, TerminationHandler);
|
||||
signal(SIGTERM, TerminationHandler);
|
||||
signal(SIGQUIT, TerminationHandler);
|
||||
|
||||
// Become a background process in its own process group by default.
|
||||
if ( !foreground )
|
||||
{
|
||||
pid_t child_pid = fork();
|
||||
if ( child_pid < 0 )
|
||||
err(1, "fork");
|
||||
if ( child_pid )
|
||||
exit(0);
|
||||
setpgid(0, 0);
|
||||
}
|
||||
else
|
||||
ready();
|
||||
|
||||
// Listen for filesystem messages.
|
||||
int channel;
|
||||
while ( 0 <= (channel = accept(serverfd, NULL, NULL)) )
|
||||
{
|
||||
if ( should_terminate )
|
||||
break;
|
||||
struct fsm_msg_header hdr;
|
||||
size_t amount;
|
||||
if ( (amount = readall(channel, &hdr, sizeof(hdr))) != sizeof(hdr) )
|
||||
{
|
||||
//warn("incomplete header: got %zi of %zu bytes", amount, sizeof(hdr));
|
||||
errno = 0;
|
||||
continue;
|
||||
}
|
||||
HandleIncomingMessage(channel, &hdr, fs);
|
||||
close(channel);
|
||||
}
|
||||
|
||||
// Garbage collect all open inode references.
|
||||
while ( fs->mru_inode )
|
||||
{
|
||||
Inode* inode = fs->mru_inode;
|
||||
if ( inode->remote_reference_count )
|
||||
inode->RemoteUnref();
|
||||
else if ( inode->reference_count )
|
||||
inode->Unref();
|
||||
}
|
||||
|
||||
close(serverfd);
|
||||
|
||||
delete fs;
|
||||
delete dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
31
iso9660/fsmarshall.h
Normal file
31
iso9660/fsmarshall.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* fsmarshall.h
|
||||
* Sortix fsmarshall frontend.
|
||||
*/
|
||||
|
||||
#ifndef FSMARSHALL_H
|
||||
#define FSMARSHALL_H
|
||||
|
||||
class Device;
|
||||
class Filesystem;
|
||||
|
||||
int fsmarshall_main(const char* argv0,
|
||||
const char* mount_path,
|
||||
bool foreground,
|
||||
Filesystem* fs,
|
||||
Device* dev);
|
||||
#endif
|
572
iso9660/fuse.cpp
Normal file
572
iso9660/fuse.cpp
Normal file
|
@ -0,0 +1,572 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* fuse.cpp
|
||||
* FUSE frontend.
|
||||
*/
|
||||
|
||||
#if !defined(__sortix__)
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define FUSE_USE_VERSION 26
|
||||
#include <fuse.h>
|
||||
|
||||
#include "block.h"
|
||||
#include "device.h"
|
||||
#include "filesystem.h"
|
||||
#include "fuse.h"
|
||||
#include "inode.h"
|
||||
#include "iso9660fs.h"
|
||||
|
||||
#include <stdio.h> // DEBUG
|
||||
|
||||
struct iso9660_fuse_ctx
|
||||
{
|
||||
Device* dev;
|
||||
Filesystem* fs;
|
||||
};
|
||||
|
||||
#ifndef S_SETABLE
|
||||
#define S_SETABLE 02777
|
||||
#endif
|
||||
|
||||
#define FUSE_FS (((struct iso9660_fuse_ctx*) (fuse_get_context()->private_data))->fs)
|
||||
|
||||
void* iso9660_fuse_init(struct fuse_conn_info* /*conn*/)
|
||||
{
|
||||
return fuse_get_context()->private_data;
|
||||
}
|
||||
|
||||
void iso9660_fuse_destroy(void* fs_private)
|
||||
{
|
||||
struct iso9660_fuse_ctx* iso9660_fuse_ctx =
|
||||
(struct iso9660_fuse_ctx*) fs_private;
|
||||
while ( iso9660_fuse_ctx->fs->mru_inode )
|
||||
{
|
||||
Inode* inode = iso9660_fuse_ctx->fs->mru_inode;
|
||||
if ( inode->remote_reference_count )
|
||||
inode->RemoteUnref();
|
||||
else if ( inode->reference_count )
|
||||
inode->Unref();
|
||||
}
|
||||
delete iso9660_fuse_ctx->fs; iso9660_fuse_ctx->fs = NULL;
|
||||
delete iso9660_fuse_ctx->dev; iso9660_fuse_ctx->dev = NULL;
|
||||
}
|
||||
|
||||
Inode* iso9660_fuse_resolve_path(const char* path)
|
||||
{
|
||||
Filesystem* fs = FUSE_FS;
|
||||
Inode* inode = fs->GetInode(fs->root_ino);
|
||||
if ( !inode )
|
||||
return (Inode*) NULL;
|
||||
while ( path[0] )
|
||||
{
|
||||
if ( *path == '/' )
|
||||
{
|
||||
if ( !ISO9660_S_ISDIR(inode->Mode()) )
|
||||
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
|
||||
path++;
|
||||
continue;
|
||||
}
|
||||
size_t elem_len = strcspn(path, "/");
|
||||
char* elem = strndup(path, elem_len);
|
||||
if ( !elem )
|
||||
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
|
||||
path += elem_len;
|
||||
Inode* next = inode->Open(elem, O_RDONLY, 0);
|
||||
free(elem);
|
||||
inode->Unref();
|
||||
if ( !next )
|
||||
return NULL;
|
||||
inode = next;
|
||||
}
|
||||
return inode;
|
||||
}
|
||||
|
||||
// Assumes that the path doesn't end with / unless it's the root directory.
|
||||
Inode* iso9660_fuse_parent_dir(const char** path_ptr)
|
||||
{
|
||||
const char* path = *path_ptr;
|
||||
Filesystem* fs = FUSE_FS;
|
||||
Inode* inode = fs->GetInode(fs->root_ino);
|
||||
if ( !inode )
|
||||
return (Inode*) NULL;
|
||||
while ( strchr(path, '/') )
|
||||
{
|
||||
if ( *path == '/' )
|
||||
{
|
||||
if ( !ISO9660_S_ISDIR(inode->Mode()) )
|
||||
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
|
||||
path++;
|
||||
continue;
|
||||
}
|
||||
size_t elem_len = strcspn(path, "/");
|
||||
char* elem = strndup(path, elem_len);
|
||||
if ( !elem )
|
||||
return inode->Unref(), errno = ENOTDIR, (Inode*) NULL;
|
||||
path += elem_len;
|
||||
Inode* next = inode->Open(elem, O_RDONLY, 0);
|
||||
free(elem);
|
||||
inode->Unref();
|
||||
if ( !next )
|
||||
return (Inode*) NULL;
|
||||
inode = next;
|
||||
}
|
||||
*path_ptr = *path ? path : ".";
|
||||
assert(!strchr(*path_ptr, '/'));
|
||||
return inode;
|
||||
}
|
||||
|
||||
int iso9660_fuse_getattr(const char* path, struct stat* st)
|
||||
{
|
||||
Inode* inode = iso9660_fuse_resolve_path(path);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
StatInode(inode, st);
|
||||
inode->Unref();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iso9660_fuse_fgetattr(const char* /*path*/, struct stat* st,
|
||||
struct fuse_file_info* fi)
|
||||
{
|
||||
Filesystem* fs = FUSE_FS;
|
||||
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
StatInode(inode, st);
|
||||
inode->Unref();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iso9660_fuse_readlink(const char* path, char* buf, size_t bufsize)
|
||||
{
|
||||
Inode* inode = iso9660_fuse_resolve_path(path);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
if ( !ISO9660_S_ISLNK(inode->Mode()) )
|
||||
return inode->Unref(), -(errno = EINVAL);
|
||||
if ( !bufsize )
|
||||
return inode->Unref(), -(errno = EINVAL);
|
||||
ssize_t amount = inode->ReadLink((uint8_t*) buf, bufsize);
|
||||
if ( amount < 0 )
|
||||
return inode->Unref(), -errno;
|
||||
buf[(size_t) amount < bufsize ? (size_t) amount : bufsize - 1] = '\0';
|
||||
inode->Unref();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iso9660_fuse_mknod(const char* path, mode_t mode, dev_t dev)
|
||||
{
|
||||
(void) path;
|
||||
(void) mode;
|
||||
(void) dev;
|
||||
return -(errno = ENOSYS);
|
||||
}
|
||||
|
||||
int iso9660_fuse_mkdir(const char* path, mode_t /*mode*/)
|
||||
{
|
||||
Inode* inode = iso9660_fuse_parent_dir(&path);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
inode->Unref();
|
||||
return -(errno = EROFS);
|
||||
}
|
||||
|
||||
int iso9660_fuse_unlink(const char* path)
|
||||
{
|
||||
Inode* inode = iso9660_fuse_parent_dir(&path);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
bool success = inode->Unlink(path, false);
|
||||
inode->Unref();
|
||||
return success ? 0 : -errno;
|
||||
}
|
||||
|
||||
int iso9660_fuse_rmdir(const char* path)
|
||||
{
|
||||
Inode* inode = iso9660_fuse_parent_dir(&path);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
bool success = inode->RemoveDirectory(path);
|
||||
inode->Unref();
|
||||
return success ? 0 : -errno;
|
||||
}
|
||||
|
||||
int iso9660_fuse_symlink(const char* /*oldname*/, const char* newname)
|
||||
{
|
||||
Inode* newdir = iso9660_fuse_parent_dir(&newname);
|
||||
if ( !newdir )
|
||||
return -errno;
|
||||
newdir->Unref();
|
||||
return -(errno = EROFS);
|
||||
}
|
||||
|
||||
int iso9660_fuse_rename(const char* oldname, const char* newname)
|
||||
{
|
||||
Inode* olddir = iso9660_fuse_parent_dir(&oldname);
|
||||
if ( !olddir )
|
||||
return -errno;
|
||||
Inode* newdir = iso9660_fuse_parent_dir(&newname);
|
||||
if ( !newdir )
|
||||
return olddir->Unref(), -errno;
|
||||
bool success = newdir->Rename(olddir, oldname, newname);
|
||||
newdir->Unref();
|
||||
olddir->Unref();
|
||||
return success ? 0 : -errno;
|
||||
}
|
||||
|
||||
int iso9660_fuse_link(const char* oldname, const char* newname)
|
||||
{
|
||||
Inode* inode = iso9660_fuse_resolve_path(oldname);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
Inode* newdir = iso9660_fuse_parent_dir(&newname);
|
||||
if ( !newdir )
|
||||
return inode->Unref(), -errno;
|
||||
bool success = inode->Link(newname, inode);
|
||||
newdir->Unref();
|
||||
inode->Unref();
|
||||
return success ? 0 : -errno;
|
||||
}
|
||||
|
||||
int iso9660_fuse_chmod(const char* path, mode_t mode)
|
||||
{
|
||||
Inode* inode = iso9660_fuse_resolve_path(path);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
(void) mode;
|
||||
return inode->Unref(), -(errno = EROFS);
|
||||
}
|
||||
|
||||
int iso9660_fuse_chown(const char* path, uid_t owner, gid_t group)
|
||||
{
|
||||
Inode* inode = iso9660_fuse_resolve_path(path);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
(void) owner;
|
||||
(void) group;
|
||||
return inode->Unref(), -(errno = EROFS);
|
||||
}
|
||||
|
||||
int iso9660_fuse_truncate(const char* path, off_t size)
|
||||
{
|
||||
Inode* inode = iso9660_fuse_resolve_path(path);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
(void) size;
|
||||
return inode->Unref(), -(errno = EROFS);
|
||||
}
|
||||
|
||||
int iso9660_fuse_ftruncate(const char* /*path*/, off_t size,
|
||||
struct fuse_file_info* fi)
|
||||
{
|
||||
Filesystem* fs = FUSE_FS;
|
||||
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
(void) size;
|
||||
return inode->Unref(), -(errno = EROFS);
|
||||
}
|
||||
|
||||
int iso9660_fuse_open(const char* path, struct fuse_file_info* fi)
|
||||
{
|
||||
int flags = fi->flags;
|
||||
Inode* dir = iso9660_fuse_parent_dir(&path);
|
||||
if ( !dir )
|
||||
return -errno;
|
||||
Inode* result = dir->Open(path, flags, 0);
|
||||
dir->Unref();
|
||||
if ( !result )
|
||||
return -errno;
|
||||
fi->fh = (uint64_t) result->inode_id;
|
||||
fi->keep_cache = 1;
|
||||
result->RemoteRefer();
|
||||
result->Unref();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iso9660_fuse_access(const char* path, int mode)
|
||||
{
|
||||
Inode* dir = iso9660_fuse_parent_dir(&path);
|
||||
if ( !dir )
|
||||
return -errno;
|
||||
Inode* result = dir->Open(path, O_RDONLY, 0);
|
||||
dir->Unref();
|
||||
if ( !result )
|
||||
return -errno;
|
||||
(void) mode;
|
||||
result->Unref();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iso9660_fuse_create(const char* path, mode_t mode, struct fuse_file_info* fi)
|
||||
{
|
||||
int flags = fi->flags | O_CREAT;
|
||||
Inode* inode = iso9660_fuse_parent_dir(&path);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
Inode* result = inode->Open(path, flags, FsModeFromHostMode(mode));
|
||||
inode->Unref();
|
||||
if ( !result )
|
||||
return -errno;
|
||||
fi->fh = (uint64_t) result->inode_id;
|
||||
fi->keep_cache = 1;
|
||||
result->RemoteRefer();
|
||||
result->Unref();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iso9660_fuse_opendir(const char* path, struct fuse_file_info* fi)
|
||||
{
|
||||
return iso9660_fuse_open(path, fi);
|
||||
}
|
||||
|
||||
int iso9660_fuse_read(const char* /*path*/, char* buf, size_t count,
|
||||
off_t offset, struct fuse_file_info* fi)
|
||||
{
|
||||
Filesystem* fs = FUSE_FS;
|
||||
if ( INT_MAX < count )
|
||||
count = INT_MAX;
|
||||
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
ssize_t result = inode->ReadAt((uint8_t*) buf, count, offset);
|
||||
inode->Unref();
|
||||
return 0 <= result ? (int) result : -errno;
|
||||
}
|
||||
|
||||
int iso9660_fuse_write(const char* /*path*/, const char* /*buf*/,
|
||||
size_t /*count*/, off_t /*offset*/,
|
||||
struct fuse_file_info* fi)
|
||||
{
|
||||
Filesystem* fs = FUSE_FS;
|
||||
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
inode->Unref();
|
||||
return -(errno = EROFS);
|
||||
}
|
||||
|
||||
int iso9660_fuse_statfs(const char* /*path*/, struct statvfs* stvfs)
|
||||
{
|
||||
memset(stvfs, 0, sizeof(*stvfs));
|
||||
Filesystem* fs = FUSE_FS;
|
||||
stvfs->f_bsize = fs->block_size;
|
||||
stvfs->f_frsize = fs->block_size;
|
||||
stvfs->f_blocks = fs->device->device_size / fs->block_size;
|
||||
stvfs->f_bfree = 0;
|
||||
stvfs->f_bavail = 0;
|
||||
stvfs->f_files = 0;
|
||||
stvfs->f_ffree = 0;
|
||||
stvfs->f_favail = 0;
|
||||
stvfs->f_ffree = 0;
|
||||
stvfs->f_fsid = 0;
|
||||
stvfs->f_flag = ST_RDONLY;
|
||||
stvfs->f_namemax = 255;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iso9660_fuse_flush(const char* /*path*/, struct fuse_file_info* fi)
|
||||
{
|
||||
Filesystem* fs = FUSE_FS;
|
||||
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
inode->Unref();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iso9660_fuse_release(const char* /*path*/, struct fuse_file_info* fi)
|
||||
{
|
||||
Filesystem* fs = FUSE_FS;
|
||||
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
inode->RemoteUnref();
|
||||
inode->Unref();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iso9660_fuse_releasedir(const char* path, struct fuse_file_info* fi)
|
||||
{
|
||||
return iso9660_fuse_release(path, fi);
|
||||
}
|
||||
|
||||
int iso9660_fuse_fsync(const char* /*path*/, int data, struct fuse_file_info* fi)
|
||||
{
|
||||
(void) data;
|
||||
Filesystem* fs = FUSE_FS;
|
||||
Inode* inode = fs->GetInode((uint32_t) fi->fh);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
inode->Unref();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*int iso9660_fuse_syncdir(const char* path, int data, struct fuse_file_info* fi)
|
||||
{
|
||||
return iso9660_fuse_sync(path, data, fi);
|
||||
}*/
|
||||
|
||||
/*int iso9660_fuse_setxattr(const char *, const char *, const char *, size_t, int)
|
||||
{
|
||||
return -(errno = ENOSYS);
|
||||
}*/
|
||||
|
||||
/*int iso9660_fuse_getxattr(const char *, const char *, char *, size_t)
|
||||
{
|
||||
return -(errno = ENOSYS);
|
||||
}*/
|
||||
|
||||
/*int iso9660_fuse_listxattr(const char *, char *, size_t)
|
||||
{
|
||||
return -(errno = ENOSYS);
|
||||
}*/
|
||||
|
||||
/*int iso9660_fuse_removexattr(const char *, const char *)
|
||||
{
|
||||
return -(errno = ENOSYS);
|
||||
}*/
|
||||
|
||||
int iso9660_fuse_readdir(const char* /*path*/, void* buf,
|
||||
fuse_fill_dir_t filler, off_t rec_num,
|
||||
struct fuse_file_info* fi)
|
||||
{
|
||||
Filesystem* fs = FUSE_FS;
|
||||
Inode* inode = fs->GetInode((iso9660_ino_t) fi->fh);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
if ( !S_ISDIR(inode->Mode()) )
|
||||
return inode->Unref(), -(errno = ENOTDIR);
|
||||
uint64_t offset = 0;
|
||||
Block* block = NULL;
|
||||
uint64_t block_id = 0;
|
||||
char name[256];
|
||||
uint8_t file_type;
|
||||
iso9660_ino_t inode_id;
|
||||
while ( inode->ReadDirectory(&offset, &block, &block_id,
|
||||
rec_num ? NULL : name, &file_type, &inode_id) )
|
||||
{
|
||||
if ( !rec_num || !rec_num-- )
|
||||
{
|
||||
if ( filler(buf, name, NULL, 0) )
|
||||
{
|
||||
block->Unref();
|
||||
inode->Unref();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
int errnum = errno;
|
||||
if ( block )
|
||||
block->Unref();
|
||||
inode->Unref();
|
||||
if ( errnum )
|
||||
return -errnum;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*int iso9660_fuse_lock(const char*, struct fuse_file_info*, int, struct flock*)
|
||||
{
|
||||
return -(errno = ENOSYS);
|
||||
}*/
|
||||
|
||||
int iso9660_fuse_utimens(const char* path, const struct timespec tv[2])
|
||||
{
|
||||
Inode* inode = iso9660_fuse_resolve_path(path);
|
||||
if ( !inode )
|
||||
return -errno;
|
||||
(void) tv;
|
||||
return inode->Unref(), -(errno = EROFS);
|
||||
}
|
||||
|
||||
/*int iso9660_fuse_bmap(const char*, size_t blocksize, uint64_t* idx)
|
||||
{
|
||||
return -(errno = ENOSYS);
|
||||
}*/
|
||||
|
||||
int iso9660_fuse_main(const char* argv0,
|
||||
const char* mount_path,
|
||||
bool foreground,
|
||||
Filesystem* fs,
|
||||
Device* dev)
|
||||
{
|
||||
struct fuse_operations operations;
|
||||
memset(&operations, 0, sizeof(operations));
|
||||
|
||||
operations.access = iso9660_fuse_access;
|
||||
operations.chmod = iso9660_fuse_chmod;
|
||||
operations.chown = iso9660_fuse_chown;
|
||||
operations.create = iso9660_fuse_create;
|
||||
operations.destroy = iso9660_fuse_destroy;
|
||||
operations.fgetattr = iso9660_fuse_fgetattr;
|
||||
operations.flush = iso9660_fuse_flush;
|
||||
operations.fsync = iso9660_fuse_fsync;
|
||||
operations.ftruncate = iso9660_fuse_ftruncate;
|
||||
operations.getattr = iso9660_fuse_getattr;
|
||||
operations.init = iso9660_fuse_init;
|
||||
operations.link = iso9660_fuse_link;
|
||||
operations.mkdir = iso9660_fuse_mkdir;
|
||||
operations.mknod = iso9660_fuse_mknod;
|
||||
operations.opendir = iso9660_fuse_opendir;
|
||||
operations.open = iso9660_fuse_open;
|
||||
operations.readdir = iso9660_fuse_readdir;
|
||||
operations.read = iso9660_fuse_read;
|
||||
operations.readlink = iso9660_fuse_readlink;
|
||||
operations.releasedir = iso9660_fuse_releasedir;
|
||||
operations.release = iso9660_fuse_release;
|
||||
operations.rename = iso9660_fuse_rename;
|
||||
operations.rmdir = iso9660_fuse_rmdir;
|
||||
operations.statfs = iso9660_fuse_statfs;
|
||||
operations.symlink = iso9660_fuse_symlink;
|
||||
operations.truncate = iso9660_fuse_truncate;
|
||||
operations.unlink = iso9660_fuse_unlink;
|
||||
operations.utimens = iso9660_fuse_utimens;
|
||||
operations.write = iso9660_fuse_write;
|
||||
|
||||
operations.flag_nullpath_ok = 1;
|
||||
operations.flag_nopath = 1;
|
||||
|
||||
char* argv_fuse[] =
|
||||
{
|
||||
(char*) argv0,
|
||||
(char*) "-s",
|
||||
(char*) (foreground ? "-f" : mount_path),
|
||||
(char*) (foreground ? mount_path : NULL),
|
||||
(char*) NULL,
|
||||
};
|
||||
|
||||
int argc_fuse = sizeof(argv_fuse) / sizeof(argv_fuse[0]) - 1;
|
||||
|
||||
struct iso9660_fuse_ctx iso9660_fuse_ctx;
|
||||
iso9660_fuse_ctx.fs = fs;
|
||||
iso9660_fuse_ctx.dev = dev;
|
||||
|
||||
return fuse_main(argc_fuse, argv_fuse, &operations, &iso9660_fuse_ctx);
|
||||
}
|
||||
|
||||
#endif
|
32
iso9660/fuse.h
Normal file
32
iso9660/fuse.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Copyright (c) 2015 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* fuse.h
|
||||
* FUSE frontend.
|
||||
*/
|
||||
|
||||
#ifndef FUSE_H
|
||||
#define FUSE_H
|
||||
|
||||
class Device;
|
||||
class Filesystem;
|
||||
|
||||
int iso9660_fuse_main(const char* argv0,
|
||||
const char* mount_path,
|
||||
bool foreground,
|
||||
Filesystem* fs,
|
||||
Device* dev);
|
||||
|
||||
#endif
|
614
iso9660/inode.cpp
Normal file
614
iso9660/inode.cpp
Normal file
|
@ -0,0 +1,614 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2014, 2015, 2018, 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* inode.cpp
|
||||
* Filesystem inode.
|
||||
*/
|
||||
|
||||
#define __STDC_CONSTANT_MACROS
|
||||
#define __STDC_LIMIT_MACROS
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <endian.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "block.h"
|
||||
#include "device.h"
|
||||
#include "filesystem.h"
|
||||
#include "inode.h"
|
||||
#include "iso9660fs.h"
|
||||
#include "util.h"
|
||||
|
||||
#ifndef S_SETABLE
|
||||
#define S_SETABLE 02777
|
||||
#endif
|
||||
#ifndef O_WRITE
|
||||
#define O_WRITE (O_WRONLY | O_RDWR)
|
||||
#endif
|
||||
|
||||
Inode::Inode(Filesystem* filesystem, iso9660_ino_t inode_id)
|
||||
{
|
||||
this->prev_inode = NULL;
|
||||
this->next_inode = NULL;
|
||||
this->prev_hashed = NULL;
|
||||
this->next_hashed = NULL;
|
||||
this->data_block = NULL;
|
||||
this->data = NULL;
|
||||
this->filesystem = filesystem;
|
||||
this->reference_count = 1;
|
||||
this->remote_reference_count = 0;
|
||||
this->inode_id = inode_id;
|
||||
this->parent_inode_id = 0;
|
||||
}
|
||||
|
||||
Inode::~Inode()
|
||||
{
|
||||
if ( data_block )
|
||||
data_block->Unref();
|
||||
Unlink();
|
||||
}
|
||||
|
||||
void Inode::Parse()
|
||||
{
|
||||
const uint8_t* block_data = (const uint8_t*) data;
|
||||
uid = 0;
|
||||
gid = 0;
|
||||
time = 0;
|
||||
uint8_t file_flags = block_data[25];
|
||||
bool is_directory = file_flags & ISO9660_DIRENT_FLAG_DIR;
|
||||
mode = 0555 | (is_directory ? ISO9660_S_IFDIR : ISO9660_S_IFREG);
|
||||
uint32_t u32;
|
||||
memcpy(&u32, block_data + 10, sizeof(u32));
|
||||
size = le32toh(u32);
|
||||
nlink = 1;
|
||||
const uint8_t* time_bytes = block_data + 18;
|
||||
struct tm tm;
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
tm.tm_year = time_bytes[0];
|
||||
tm.tm_mon = time_bytes[1] - 1;
|
||||
tm.tm_mday = time_bytes[2];
|
||||
tm.tm_hour = time_bytes[3];
|
||||
tm.tm_min = time_bytes[4];
|
||||
tm.tm_sec = time_bytes[5];
|
||||
// TODO: Is this accurate?
|
||||
time_t tz_offset = (-48 + time_bytes[6]) * 15 * 60;
|
||||
// TODO: The timezone offset should've been mixed in with mktime, somehow.
|
||||
time = mktime(&tm) + tz_offset;
|
||||
uint8_t dirent_len = block_data[0];
|
||||
uint8_t name_len = block_data[32];
|
||||
size_t extended_off = 33 + name_len + !(name_len & 1);
|
||||
for ( size_t i = extended_off; i < dirent_len && 3 <= dirent_len - i; )
|
||||
{
|
||||
const uint8_t* field = block_data + i;
|
||||
uint8_t len = field[2];
|
||||
if ( !len || dirent_len - i < len )
|
||||
break;
|
||||
i += len;
|
||||
if ( len == 36 && field[0] == 'P' && field[1] == 'X' && field[3] == 1 )
|
||||
{
|
||||
uint32_t bits;
|
||||
memcpy(&bits, field + 4, sizeof(bits));
|
||||
mode = le32toh(bits) & 0xffff;
|
||||
memcpy(&bits, field + 12, sizeof(bits));
|
||||
nlink = le32toh(bits);
|
||||
memcpy(&bits, field + 20, sizeof(bits));
|
||||
uid = le32toh(bits);
|
||||
memcpy(&bits, field + 28, sizeof(bits));
|
||||
gid = le32toh(bits);
|
||||
}
|
||||
}
|
||||
if ( ISO9660_S_ISLNK(mode) )
|
||||
{
|
||||
uint8_t buf[256];
|
||||
size = ReadLink(buf, sizeof(buf));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t Inode::Mode()
|
||||
{
|
||||
return mode;
|
||||
}
|
||||
|
||||
uint32_t Inode::UserId()
|
||||
{
|
||||
return uid;
|
||||
}
|
||||
|
||||
uint32_t Inode::GroupId()
|
||||
{
|
||||
return gid;
|
||||
}
|
||||
|
||||
uint64_t Inode::Size()
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
uint64_t Inode::Time()
|
||||
{
|
||||
return time;
|
||||
}
|
||||
|
||||
Block* Inode::GetBlock(uint32_t offset)
|
||||
{
|
||||
uint32_t lba;
|
||||
memcpy(&lba, (uint8_t*) data + 2, sizeof(lba));
|
||||
lba = le32toh(lba);
|
||||
uint32_t block_id = lba + offset;
|
||||
return filesystem->device->GetBlock(block_id);
|
||||
}
|
||||
|
||||
bool Inode::FindParentInode(uint64_t parent_lba, uint64_t parent_size)
|
||||
{
|
||||
if ( inode_id == filesystem->root_ino )
|
||||
return parent_inode_id = inode_id, true;
|
||||
Block* block = NULL;
|
||||
uint32_t block_size = filesystem->block_size;
|
||||
uint64_t parent_offset = parent_lba * block_size;
|
||||
uint64_t block_id = 0;
|
||||
uint64_t offset = 0;
|
||||
while ( offset < parent_size )
|
||||
{
|
||||
uint64_t entry_block_id = (parent_offset + offset) / block_size;
|
||||
uint64_t entry_block_offset = (parent_offset + offset) % block_size;
|
||||
if ( block && block_id != entry_block_id )
|
||||
{
|
||||
block->Unref();
|
||||
block = NULL;
|
||||
}
|
||||
if ( !block && !(block = filesystem->device->GetBlock(entry_block_id)) )
|
||||
return false;
|
||||
const uint8_t* block_data = block->block_data + entry_block_offset;
|
||||
uint8_t dirent_len = block_data[0];
|
||||
if ( !dirent_len )
|
||||
{
|
||||
offset = (entry_block_id + 1) * block_size;
|
||||
continue;
|
||||
}
|
||||
uint8_t name_len = block_data[32];
|
||||
const uint8_t* name_data = block_data + 33;
|
||||
if ( name_len == 0 || !name_data[0] )
|
||||
{
|
||||
parent_inode_id = parent_offset + offset;
|
||||
return true;
|
||||
}
|
||||
// TODO: Can dirent_len be misaligned?
|
||||
uint64_t reclen = dirent_len + (dirent_len & 1);
|
||||
if ( !reclen )
|
||||
return errno = EINVAL, false;
|
||||
offset += reclen;
|
||||
}
|
||||
return errno = EINVAL, false;
|
||||
}
|
||||
|
||||
bool Inode::ReadDirectory(uint64_t* offset_inout,
|
||||
Block** block_inout,
|
||||
uint64_t* block_id_inout,
|
||||
char* name,
|
||||
uint8_t* file_type_out,
|
||||
iso9660_ino_t* inode_id_out)
|
||||
{
|
||||
uint64_t offset = *offset_inout;
|
||||
next_block:
|
||||
uint64_t filesize = Size();
|
||||
if ( filesize <= offset )
|
||||
return errno = 0, false;
|
||||
uint64_t entry_block_id = offset / filesystem->block_size;
|
||||
uint64_t entry_block_offset = offset % filesystem->block_size;
|
||||
if ( *block_inout && *block_id_inout != entry_block_id )
|
||||
{
|
||||
(*block_inout)->Unref();
|
||||
(*block_inout) = NULL;
|
||||
}
|
||||
if ( !*block_inout &&
|
||||
!(*block_inout = GetBlock(*block_id_inout = entry_block_id)) )
|
||||
return false;
|
||||
const uint8_t* block_data = (*block_inout)->block_data + entry_block_offset;
|
||||
uint8_t dirent_len = block_data[0];
|
||||
if ( !dirent_len )
|
||||
{
|
||||
offset = (entry_block_id + 1) * filesystem->block_size;
|
||||
goto next_block;
|
||||
}
|
||||
if ( name )
|
||||
{
|
||||
uint8_t name_len = block_data[32];
|
||||
const uint8_t* name_data = block_data + 33;
|
||||
size_t extended_off = 33 + name_len + !(name_len & 1);
|
||||
iso9660_ino_t entry_inode_id =
|
||||
((*block_inout)->block_id * filesystem->block_size) +
|
||||
entry_block_offset;
|
||||
// TODO: The root directory inode should be that of its . entry.
|
||||
if ( name_len == 0 || !name_data[0] )
|
||||
{
|
||||
name[0] = '.';
|
||||
name[1] = '\0';
|
||||
name_len = 1;
|
||||
entry_inode_id = inode_id;
|
||||
}
|
||||
else if ( name_len == 1 && name_data[0] == 1 )
|
||||
{
|
||||
name[0] = '.';
|
||||
name[1] = '.';
|
||||
name[2] = '\0';
|
||||
name_len = 2;
|
||||
if ( !parent_inode_id )
|
||||
{
|
||||
uint32_t parent_lba;
|
||||
memcpy(&parent_lba, block_data + 2, sizeof(parent_lba));
|
||||
parent_lba = le32toh(parent_lba);
|
||||
uint32_t parent_size;
|
||||
memcpy(&parent_size, block_data + 10, sizeof(parent_size));
|
||||
parent_size = le32toh(parent_size);
|
||||
if ( !FindParentInode(parent_lba, parent_size) )
|
||||
return false;
|
||||
}
|
||||
entry_inode_id = parent_inode_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( size_t i = 0; i < name_len; i++ )
|
||||
{
|
||||
if ( name_data[i] == ';' )
|
||||
{
|
||||
name_len = i;
|
||||
break;
|
||||
}
|
||||
name[i] = tolower(name_data[i]);
|
||||
}
|
||||
name[name_len] = '\0';
|
||||
}
|
||||
for ( size_t i = extended_off; i < dirent_len && 3 <= dirent_len - i; )
|
||||
{
|
||||
const uint8_t* field = block_data + i;
|
||||
uint8_t len = field[2];
|
||||
if ( !len || dirent_len - i < len )
|
||||
break;
|
||||
i += len;
|
||||
if ( 5 <= len && field[0] == 'N' && field[1] == 'M' &&
|
||||
field[3] == 1 )
|
||||
{
|
||||
uint8_t nm_flags = field[4];
|
||||
if ( nm_flags & (1 << 0) ) // TODO: Continued names.
|
||||
break;
|
||||
name_len = len - 5;
|
||||
memcpy(name, field + 5, name_len);
|
||||
name[name_len] = '\0';
|
||||
}
|
||||
// TODO: Other extensions.
|
||||
}
|
||||
uint8_t file_flags = block_data[25];
|
||||
// TODO: Rock Ridge.
|
||||
bool is_directory = file_flags & ISO9660_DIRENT_FLAG_DIR;
|
||||
uint8_t file_type = is_directory ? ISO9660_FT_DIR : ISO9660_FT_REG_FILE;
|
||||
*file_type_out = file_type;
|
||||
*inode_id_out = entry_inode_id;
|
||||
}
|
||||
// TODO: Can dirent_len be misaligned?
|
||||
uint64_t reclen = dirent_len + (dirent_len & 1);
|
||||
if ( !reclen )
|
||||
return errno = EINVAL, false;
|
||||
offset += reclen;
|
||||
*offset_inout = offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
Inode* Inode::Open(const char* elem, int flags, mode_t mode)
|
||||
{
|
||||
if ( !ISO9660_S_ISDIR(Mode()) )
|
||||
return errno = ENOTDIR, (Inode*) NULL;
|
||||
size_t elem_length = strlen(elem);
|
||||
if ( elem_length == 0 )
|
||||
return errno = ENOENT, (Inode*) NULL;
|
||||
uint64_t offset = 0;
|
||||
Block* block = NULL;
|
||||
uint64_t block_id = 0;
|
||||
char name[256];
|
||||
uint8_t file_type;
|
||||
iso9660_ino_t inode_id;
|
||||
while ( ReadDirectory(&offset, &block, &block_id, name, &file_type,
|
||||
&inode_id) )
|
||||
{
|
||||
size_t name_len = strlen(name);
|
||||
if ( name_len == elem_length && memcmp(elem, name, elem_length) == 0 )
|
||||
{
|
||||
block->Unref();
|
||||
if ( (flags & O_CREAT) && (flags & O_EXCL) )
|
||||
return errno = EEXIST, (Inode*) NULL;
|
||||
if ( (flags & O_DIRECTORY) &&
|
||||
file_type != ISO9660_FT_UNKNOWN &&
|
||||
file_type != ISO9660_FT_DIR &&
|
||||
file_type != ISO9660_FT_SYMLINK )
|
||||
return errno = ENOTDIR, (Inode*) NULL;
|
||||
Inode* inode = filesystem->GetInode(inode_id);
|
||||
if ( !inode )
|
||||
return (Inode*) NULL;
|
||||
if ( flags & O_DIRECTORY &&
|
||||
!ISO9660_S_ISDIR(inode->Mode()) &&
|
||||
!ISO9660_S_ISLNK(inode->Mode()) )
|
||||
{
|
||||
inode->Unref();
|
||||
return errno = ENOTDIR, (Inode*) NULL;
|
||||
}
|
||||
if ( flags & O_WRITE )
|
||||
{
|
||||
inode->Unref();
|
||||
return errno = EROFS, (Inode*) NULL;
|
||||
}
|
||||
return inode;
|
||||
}
|
||||
}
|
||||
if ( block )
|
||||
block->Unref();
|
||||
if ( flags & O_CREAT )
|
||||
{
|
||||
(void) mode;
|
||||
return errno = EROFS, (Inode*) NULL;
|
||||
}
|
||||
return errno = ENOENT, (Inode*) NULL;
|
||||
}
|
||||
|
||||
bool Inode::Link(const char* elem, Inode* dest)
|
||||
{
|
||||
if ( !ISO9660_S_ISDIR(Mode()) )
|
||||
return errno = ENOTDIR, false;
|
||||
if ( ISO9660_S_ISDIR(dest->Mode()) )
|
||||
return errno = EISDIR, false;
|
||||
|
||||
size_t elem_length = strlen(elem);
|
||||
if ( elem_length == 0 )
|
||||
return errno = ENOENT, false;
|
||||
uint64_t offset = 0;
|
||||
Block* block = NULL;
|
||||
uint64_t block_id = 0;
|
||||
char name[256];
|
||||
uint8_t file_type;
|
||||
iso9660_ino_t inode_id;
|
||||
while ( ReadDirectory(&offset, &block, &block_id, name, &file_type,
|
||||
&inode_id) )
|
||||
{
|
||||
size_t name_len = strlen(name);
|
||||
if ( name_len == elem_length && memcmp(elem, name, elem_length) == 0 )
|
||||
{
|
||||
block->Unref();
|
||||
return errno = EEXIST, false;
|
||||
}
|
||||
}
|
||||
if ( block )
|
||||
block->Unref();
|
||||
return errno = EROFS, false;
|
||||
}
|
||||
|
||||
Inode* Inode::UnlinkKeep(const char* elem, bool directories, bool force)
|
||||
{
|
||||
if ( !ISO9660_S_ISDIR(Mode()) )
|
||||
return errno = ENOTDIR, (Inode*) NULL;
|
||||
size_t elem_length = strlen(elem);
|
||||
if ( elem_length == 0 )
|
||||
return errno = ENOENT, (Inode*) NULL;
|
||||
uint64_t offset = 0;
|
||||
Block* block = NULL;
|
||||
uint64_t block_id = 0;
|
||||
char name[256];
|
||||
uint8_t file_type;
|
||||
iso9660_ino_t inode_id;
|
||||
while ( ReadDirectory(&offset, &block, &block_id, name, &file_type,
|
||||
&inode_id) )
|
||||
{
|
||||
size_t name_len = strlen(name);
|
||||
if ( name_len == elem_length && memcmp(elem, name, elem_length) == 0 )
|
||||
{
|
||||
(void) directories;
|
||||
(void) force;
|
||||
block->Unref();
|
||||
return errno = EROFS, (Inode*) NULL;
|
||||
}
|
||||
}
|
||||
if ( block )
|
||||
block->Unref();
|
||||
return errno = ENOENT, (Inode*) NULL;
|
||||
}
|
||||
|
||||
bool Inode::Unlink(const char* elem, bool directories, bool force)
|
||||
{
|
||||
Inode* result = UnlinkKeep(elem, directories, force);
|
||||
if ( !result )
|
||||
return false;
|
||||
result->Unref();
|
||||
return true;
|
||||
}
|
||||
|
||||
ssize_t Inode::ReadLink(uint8_t* buf, size_t bufsize)
|
||||
{
|
||||
size_t result = 0;
|
||||
const uint8_t* block_data = (const uint8_t*) data;
|
||||
uint8_t dirent_len = block_data[0];
|
||||
uint8_t name_len = block_data[32];
|
||||
size_t extended_off = 33 + name_len + !(name_len & 1);
|
||||
bool continued = true;
|
||||
for ( size_t i = extended_off; i < dirent_len && 3 <= dirent_len - i; )
|
||||
{
|
||||
const uint8_t* field = block_data + i;
|
||||
uint8_t len = field[2];
|
||||
if ( !len || dirent_len - i < len )
|
||||
break;
|
||||
i += len;
|
||||
if ( 5 <= len && field[0] == 'S' && field[1] == 'L' && field[3] == 1 )
|
||||
{
|
||||
for ( size_t n = 5; n < len && 2 <= len - n; )
|
||||
{
|
||||
uint8_t comp_flags = field[n + 0];
|
||||
uint8_t comp_len = field[n + 1];
|
||||
if ( len - (n + 2) < comp_len )
|
||||
break;
|
||||
const char* data = (const char*) (field + n + 2);
|
||||
size_t datalen = comp_len;
|
||||
// TODO: How is a trailing slash encoded?
|
||||
if ( !continued || (comp_flags & (1 << 3) /* root */) )
|
||||
{
|
||||
buf[result++] = '/';
|
||||
if ( result == bufsize )
|
||||
return (ssize_t) result;
|
||||
}
|
||||
if ( comp_flags & (1 << 1) )
|
||||
{
|
||||
data = ".";
|
||||
datalen = 1;
|
||||
}
|
||||
else if ( comp_flags & (1 << 2) )
|
||||
{
|
||||
data = "..";
|
||||
datalen = 2;
|
||||
}
|
||||
size_t possible = bufsize - result;
|
||||
size_t count = datalen < possible ? datalen : possible;
|
||||
memcpy(buf + result, data, count);
|
||||
result += count;
|
||||
if ( result == bufsize )
|
||||
return (ssize_t) result;
|
||||
continued = comp_flags & (1 << 0);
|
||||
n += 2 + comp_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (ssize_t) result;
|
||||
}
|
||||
|
||||
ssize_t Inode::ReadAt(uint8_t* buf, size_t s_count, off_t o_offset)
|
||||
{
|
||||
if ( !ISO9660_S_ISREG(Mode()) && !ISO9660_S_ISLNK(Mode()) )
|
||||
return errno = EISDIR, -1;
|
||||
if ( o_offset < 0 )
|
||||
return errno = EINVAL, -1;
|
||||
if ( SSIZE_MAX < s_count )
|
||||
s_count = SSIZE_MAX;
|
||||
uint64_t sofar = 0;
|
||||
uint64_t count = (uint64_t) s_count;
|
||||
uint64_t offset = (uint64_t) o_offset;
|
||||
uint64_t file_size = Size();
|
||||
if ( file_size <= offset )
|
||||
return 0;
|
||||
if ( file_size - offset < count )
|
||||
count = file_size - offset;
|
||||
while ( sofar < count )
|
||||
{
|
||||
uint64_t block_id = offset / filesystem->block_size;
|
||||
uint32_t block_offset = offset % filesystem->block_size;
|
||||
uint32_t block_left = filesystem->block_size - block_offset;
|
||||
Block* block = GetBlock(block_id);
|
||||
if ( !block )
|
||||
return sofar ? sofar : -1;
|
||||
size_t amount = count - sofar < block_left ? count - sofar : block_left;
|
||||
memcpy(buf + sofar, block->block_data + block_offset, amount);
|
||||
sofar += amount;
|
||||
offset += amount;
|
||||
block->Unref();
|
||||
}
|
||||
return (ssize_t) sofar;
|
||||
}
|
||||
|
||||
bool Inode::Rename(Inode* olddir, const char* oldname, const char* newname)
|
||||
{
|
||||
if ( !strcmp(oldname, ".") || !strcmp(oldname, "..") ||
|
||||
!strcmp(newname, ".") || !strcmp(newname, "..") )
|
||||
return errno = EPERM, false;
|
||||
Inode* src_inode = olddir->Open(oldname, O_RDONLY, 0);
|
||||
if ( !src_inode )
|
||||
return false;
|
||||
if ( Inode* dst_inode = Open(newname, O_RDONLY, 0) )
|
||||
{
|
||||
bool same_inode = src_inode->inode_id == dst_inode->inode_id;
|
||||
dst_inode->Unref();
|
||||
if ( same_inode )
|
||||
return src_inode->Unref(), true;
|
||||
}
|
||||
src_inode->Unref();
|
||||
return errno = EROFS, false;
|
||||
}
|
||||
|
||||
bool Inode::RemoveDirectory(const char* path)
|
||||
{
|
||||
return UnlinkKeep(path, true);
|
||||
}
|
||||
|
||||
void Inode::Refer()
|
||||
{
|
||||
reference_count++;
|
||||
}
|
||||
|
||||
void Inode::Unref()
|
||||
{
|
||||
assert(0 < reference_count);
|
||||
reference_count--;
|
||||
if ( !reference_count && !remote_reference_count )
|
||||
delete this;
|
||||
}
|
||||
|
||||
void Inode::RemoteRefer()
|
||||
{
|
||||
remote_reference_count++;
|
||||
}
|
||||
|
||||
void Inode::RemoteUnref()
|
||||
{
|
||||
assert(0 < remote_reference_count);
|
||||
remote_reference_count--;
|
||||
if ( !reference_count && !remote_reference_count )
|
||||
delete this;
|
||||
}
|
||||
|
||||
void Inode::Use()
|
||||
{
|
||||
data_block->Use();
|
||||
Unlink();
|
||||
Prelink();
|
||||
}
|
||||
|
||||
void Inode::Unlink()
|
||||
{
|
||||
(prev_inode ? prev_inode->next_inode : filesystem->mru_inode) = next_inode;
|
||||
(next_inode ? next_inode->prev_inode : filesystem->lru_inode) = prev_inode;
|
||||
size_t bin = inode_id % INODE_HASH_LENGTH;
|
||||
(prev_hashed ? prev_hashed->next_hashed : filesystem->hash_inodes[bin]) = next_hashed;
|
||||
if ( next_hashed ) next_hashed->prev_hashed = prev_hashed;
|
||||
}
|
||||
|
||||
void Inode::Prelink()
|
||||
{
|
||||
prev_inode = NULL;
|
||||
next_inode = filesystem->mru_inode;
|
||||
if ( filesystem->mru_inode )
|
||||
filesystem->mru_inode->prev_inode = this;
|
||||
filesystem->mru_inode = this;
|
||||
if ( !filesystem->lru_inode )
|
||||
filesystem->lru_inode = this;
|
||||
size_t bin = inode_id % INODE_HASH_LENGTH;
|
||||
prev_hashed = NULL;
|
||||
next_hashed = filesystem->hash_inodes[bin];
|
||||
filesystem->hash_inodes[bin] = this;
|
||||
if ( next_hashed )
|
||||
next_hashed->prev_hashed = this;
|
||||
}
|
83
iso9660/inode.h
Normal file
83
iso9660/inode.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2014, 2015, 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* inode.h
|
||||
* Filesystem inode.
|
||||
*/
|
||||
|
||||
#ifndef INODE_H
|
||||
#define INODE_H
|
||||
|
||||
#include "iso9660.h"
|
||||
|
||||
class Block;
|
||||
class Filesystem;
|
||||
|
||||
class Inode
|
||||
{
|
||||
public:
|
||||
Inode(Filesystem* filesystem, iso9660_ino_t inode_id);
|
||||
~Inode();
|
||||
|
||||
public:
|
||||
Inode* prev_inode;
|
||||
Inode* next_inode;
|
||||
Inode* prev_hashed;
|
||||
Inode* next_hashed;
|
||||
Block* data_block;
|
||||
struct iso9660_dirent* data;
|
||||
Filesystem* filesystem;
|
||||
size_t reference_count;
|
||||
size_t remote_reference_count;
|
||||
iso9660_ino_t inode_id;
|
||||
iso9660_ino_t parent_inode_id;
|
||||
uint32_t uid;
|
||||
uint32_t gid;
|
||||
uint64_t size;
|
||||
uint64_t time;
|
||||
uint32_t mode;
|
||||
uint32_t nlink;
|
||||
|
||||
public:
|
||||
void Parse();
|
||||
uint32_t Mode();
|
||||
uint32_t UserId();
|
||||
uint32_t GroupId();
|
||||
uint64_t Size();
|
||||
uint64_t Time();
|
||||
Block* GetBlock(uint32_t offset);
|
||||
bool FindParentInode(uint64_t parent_lba, uint64_t parent_size);
|
||||
bool ReadDirectory(uint64_t* offset_inout, Block** block_inout,
|
||||
uint64_t* block_id_inout, char* name,
|
||||
uint8_t* file_type_out, iso9660_ino_t* inode_id_out);
|
||||
Inode* Open(const char* elem, int flags, mode_t mode);
|
||||
bool Link(const char* elem, Inode* dest);
|
||||
bool Unlink(const char* elem, bool directories, bool force=false);
|
||||
Inode* UnlinkKeep(const char* elem, bool directories, bool force=false);
|
||||
ssize_t ReadLink(uint8_t* buf, size_t bufsize);
|
||||
ssize_t ReadAt(uint8_t* buffer, size_t count, off_t offset);
|
||||
bool Rename(Inode* olddir, const char* oldname, const char* newname);
|
||||
bool RemoveDirectory(const char* path);
|
||||
void Refer();
|
||||
void Unref();
|
||||
void RemoteRefer();
|
||||
void RemoteUnref();
|
||||
void Use();
|
||||
void Unlink();
|
||||
void Prelink();
|
||||
|
||||
};
|
||||
|
||||
#endif
|
150
iso9660/ioleast.h
Normal file
150
iso9660/ioleast.h
Normal file
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Copyright (c) 2012, 2013, 2015 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* ioleast.h
|
||||
* Versions of {,p}{read,write} that don't return until it has returned as much
|
||||
* data as requested, end of file, or an error occurs. This is sometimes needed
|
||||
* as read(2) and write(2) is not always guaranteed to fill up the entire
|
||||
* buffer or write it all.
|
||||
*/
|
||||
|
||||
#ifndef SORTIX_COMPATIBILITY_INCLUDE_IOLEAST_H
|
||||
#define SORTIX_COMPATIBILITY_INCLUDE_IOLEAST_H
|
||||
|
||||
#if defined(__sortix__) || defined(__sortix_libc__)
|
||||
|
||||
#include_next <ioleast.h>
|
||||
|
||||
#else
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if !defined(EEOF) && defined(EIO)
|
||||
#define EEOF EIO
|
||||
#endif
|
||||
|
||||
__attribute__((unused)) static inline
|
||||
size_t readleast(int fd, void* buf_ptr, size_t least, size_t max)
|
||||
{
|
||||
assert(least <= max);
|
||||
unsigned char* buf = (unsigned char*) buf_ptr;
|
||||
size_t done = 0;
|
||||
do
|
||||
{
|
||||
ssize_t amount = read(fd, buf + done, max - done);
|
||||
if ( amount < 0 )
|
||||
return done;
|
||||
if ( !amount && done < least )
|
||||
return errno = EEOF, done;
|
||||
if ( !amount )
|
||||
break;
|
||||
done += amount;
|
||||
} while ( done < least );
|
||||
return done;
|
||||
}
|
||||
|
||||
__attribute__((unused)) static inline
|
||||
size_t writeleast(int fd, const void* buf_ptr, size_t least, size_t max)
|
||||
{
|
||||
assert(least <= max);
|
||||
const unsigned char* buf = (const unsigned char*) buf_ptr;
|
||||
size_t done = 0;
|
||||
do
|
||||
{
|
||||
ssize_t amount = write(fd, buf + done, max - done);
|
||||
if ( amount < 0 )
|
||||
return done;
|
||||
if ( !amount && done < least )
|
||||
return errno = EEOF, done;
|
||||
if ( !amount )
|
||||
break;
|
||||
done += amount;
|
||||
} while ( done < least );
|
||||
return done;
|
||||
}
|
||||
|
||||
__attribute__((unused)) static inline
|
||||
size_t preadleast(int fd, void* buf_ptr, size_t least, size_t max, off_t off)
|
||||
{
|
||||
assert(least <= max);
|
||||
unsigned char* buf = (unsigned char*) buf_ptr;
|
||||
size_t done = 0;
|
||||
do
|
||||
{
|
||||
ssize_t amount = pread(fd, buf + done, max - done, off + done);
|
||||
if ( amount < 0 )
|
||||
return done;
|
||||
if ( !amount && done < least )
|
||||
return errno = EEOF, done;
|
||||
if ( !amount )
|
||||
break;
|
||||
done += amount;
|
||||
} while ( done < least );
|
||||
return done;
|
||||
}
|
||||
|
||||
__attribute__((unused)) static inline
|
||||
size_t pwriteleast(int fd, const void* buf_ptr, size_t least, size_t max, off_t off)
|
||||
{
|
||||
assert(least <= max);
|
||||
const unsigned char* buf = (const unsigned char*) buf_ptr;
|
||||
size_t done = 0;
|
||||
do
|
||||
{
|
||||
ssize_t amount = pwrite(fd, buf + done, max - done, off + done);
|
||||
if ( amount < 0 )
|
||||
return done;
|
||||
if ( !amount && done < least )
|
||||
return errno = EEOF, done;
|
||||
if ( !amount )
|
||||
break;
|
||||
done += amount;
|
||||
} while ( done < least );
|
||||
return done;
|
||||
}
|
||||
|
||||
__attribute__((unused)) static inline
|
||||
size_t readall(int fd, void* buf, size_t count)
|
||||
{
|
||||
return readleast(fd, buf, count, count);
|
||||
}
|
||||
|
||||
__attribute__((unused)) static inline
|
||||
size_t writeall(int fd, const void* buf, size_t count)
|
||||
{
|
||||
return writeleast(fd, buf, count, count);
|
||||
}
|
||||
|
||||
__attribute__((unused)) static inline
|
||||
size_t preadall(int fd, void* buf, size_t count, off_t off)
|
||||
{
|
||||
return preadleast(fd, buf, count, count, off);
|
||||
}
|
||||
|
||||
__attribute__((unused)) static inline
|
||||
size_t pwriteall(int fd, const void* buf, size_t count, off_t off)
|
||||
{
|
||||
return pwriteleast(fd, buf, count, count, off);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
128
iso9660/iso9660.h
Normal file
128
iso9660/iso9660.h
Normal file
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* iso9660.h
|
||||
* Data structures for the ISO 9660 filesystem.
|
||||
*/
|
||||
|
||||
#ifndef ISO9660_H
|
||||
#define ISO9660_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
const uint8_t TYPE_BOOT_RECORD = 0x00;
|
||||
const uint8_t TYPE_PRIMARY_VOLUME_DESCRIPTOR = 0x01;
|
||||
const uint8_t TYPE_VOLUME_DESCRIPTOR_SET_TERMINATOR = 0xFF;
|
||||
|
||||
struct iso9660_pvd /* primary volume descriptor */
|
||||
{
|
||||
uint8_t type;
|
||||
char standard_identifier[5];
|
||||
uint8_t version;
|
||||
uint8_t unused1;
|
||||
char system_identifier[32];
|
||||
char volume_identifier[32];
|
||||
uint8_t unused2[8];
|
||||
uint32_t volume_space_size_le;
|
||||
uint32_t volume_space_size_be;
|
||||
uint8_t unused3[32];
|
||||
uint16_t volume_set_size_le;
|
||||
uint16_t volume_set_size_be;
|
||||
uint16_t volume_sequence_number_le;
|
||||
uint16_t volume_sequence_number_be;
|
||||
uint16_t logical_block_size_le;
|
||||
uint16_t logical_block_size_be;
|
||||
uint32_t path_table_size_le;
|
||||
uint32_t path_table_size_be;
|
||||
uint32_t path_table_lba_le;
|
||||
uint32_t path_table_opt_lba_le;
|
||||
uint32_t path_table_lba_be;
|
||||
uint32_t path_table_opt_lba_be;
|
||||
uint8_t root_dirent[34];
|
||||
char volume_set_identifier[128];
|
||||
char publisher_identifier[128];
|
||||
char data_preparer_identifier[128];
|
||||
char application_identifier[128];
|
||||
char copyright_file_identifier[37];
|
||||
char abstract_file_identifier[37];
|
||||
char bibliographic_file_identifier[37];
|
||||
char creation_datetime[17];
|
||||
char modification_datetime[17];
|
||||
char expiration_datetime[17];
|
||||
char effective_datetime[17];
|
||||
uint8_t file_structure_version;
|
||||
uint8_t unused4;
|
||||
uint8_t application_use[512];
|
||||
uint8_t reserved[653];
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct iso9660_pvd) == 2048,
|
||||
"sizeof(struct iso9660_pvd) == 2048");
|
||||
|
||||
typedef uint64_t iso9660_ino_t;
|
||||
|
||||
struct iso9660_dirent
|
||||
{
|
||||
uint8_t unused;
|
||||
};
|
||||
|
||||
#define ISO9660_DIRENT_FLAG_DIR (1 << 1)
|
||||
|
||||
#define ISO9660_S_IFMT (0170000)
|
||||
#define ISO9660_S_IFIFO (0010000)
|
||||
#define ISO9660_S_IFCHR (0020000)
|
||||
#define ISO9660_S_IFDIR (0040000)
|
||||
#define ISO9660_S_IFBLK (0060000)
|
||||
#define ISO9660_S_IFREG (0100000)
|
||||
#define ISO9660_S_IFLNK (0120000)
|
||||
#define ISO9660_S_IFSOCK (0140000)
|
||||
|
||||
#define ISO9660_S_ISSOCK(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFSOCK)
|
||||
#define ISO9660_S_ISLNK(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFLNK)
|
||||
#define ISO9660_S_ISREG(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFREG)
|
||||
#define ISO9660_S_ISBLK(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFBLK)
|
||||
#define ISO9660_S_ISDIR(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFDIR)
|
||||
#define ISO9660_S_ISCHR(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFCHR)
|
||||
#define ISO9660_S_ISFIFO(mode) (((mode) & ISO9660_S_IFMT) == ISO9660_S_IFIFO)
|
||||
|
||||
static const uint8_t ISO9660_FT_UNKNOWN = 0;
|
||||
static const uint8_t ISO9660_FT_REG_FILE = 1;
|
||||
static const uint8_t ISO9660_FT_DIR = 2;
|
||||
static const uint8_t ISO9660_FT_CHRDEV = 3;
|
||||
static const uint8_t ISO9660_FT_BLKDEV = 4;
|
||||
static const uint8_t ISO9660_FT_FIFO = 5;
|
||||
static const uint8_t ISO9660_FT_SOCK = 6;
|
||||
static const uint8_t ISO9660_FT_SYMLINK = 7;
|
||||
|
||||
static inline uint8_t ISO9660_FT_OF_MODE(mode_t mode)
|
||||
{
|
||||
if ( ISO9660_S_ISREG(mode) )
|
||||
return ISO9660_FT_REG_FILE;
|
||||
if ( ISO9660_S_ISDIR(mode) )
|
||||
return ISO9660_FT_DIR;
|
||||
if ( ISO9660_S_ISCHR(mode) )
|
||||
return ISO9660_FT_CHRDEV;
|
||||
if ( ISO9660_S_ISBLK(mode) )
|
||||
return ISO9660_FT_BLKDEV;
|
||||
if ( ISO9660_S_ISFIFO(mode) )
|
||||
return ISO9660_FT_FIFO;
|
||||
if ( ISO9660_S_ISSOCK(mode) )
|
||||
return ISO9660_FT_SOCK;
|
||||
if ( ISO9660_S_ISLNK(mode) )
|
||||
return ISO9660_FT_SYMLINK;
|
||||
return ISO9660_FT_UNKNOWN;
|
||||
}
|
||||
|
||||
#endif
|
278
iso9660/iso9660fs.cpp
Normal file
278
iso9660/iso9660fs.cpp
Normal file
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2014, 2015, 2016, 2022, 2023 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* iso9660fs.cpp
|
||||
* Implementation of the ISO 9660 filesystem.
|
||||
*/
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <endian.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__sortix__)
|
||||
#include "fsmarshall.h"
|
||||
#else
|
||||
#include "fuse.h"
|
||||
#endif
|
||||
|
||||
#include "block.h"
|
||||
#include "device.h"
|
||||
#include "filesystem.h"
|
||||
#include "inode.h"
|
||||
#include "ioleast.h"
|
||||
#include "iso9660.h"
|
||||
#include "iso9660fs.h"
|
||||
#include "util.h"
|
||||
|
||||
uid_t request_uid;
|
||||
uid_t request_gid;
|
||||
|
||||
mode_t HostModeFromFsMode(uint32_t mode)
|
||||
{
|
||||
mode_t hostmode = mode & 07777;
|
||||
if ( ISO9660_S_ISSOCK(mode) ) hostmode |= S_IFSOCK;
|
||||
if ( ISO9660_S_ISLNK(mode) ) hostmode |= S_IFLNK;
|
||||
if ( ISO9660_S_ISREG(mode) ) hostmode |= S_IFREG;
|
||||
if ( ISO9660_S_ISBLK(mode) ) hostmode |= S_IFBLK;
|
||||
if ( ISO9660_S_ISDIR(mode) ) hostmode |= S_IFDIR;
|
||||
if ( ISO9660_S_ISCHR(mode) ) hostmode |= S_IFCHR;
|
||||
if ( ISO9660_S_ISFIFO(mode) ) hostmode |= S_IFIFO;
|
||||
return hostmode;
|
||||
}
|
||||
|
||||
uint32_t FsModeFromHostMode(mode_t hostmode)
|
||||
{
|
||||
uint32_t mode = hostmode & 07777;
|
||||
if ( S_ISSOCK(hostmode) ) mode |= ISO9660_S_IFSOCK;
|
||||
if ( S_ISLNK(hostmode) ) mode |= ISO9660_S_IFLNK;
|
||||
if ( S_ISREG(hostmode) ) mode |= ISO9660_S_IFREG;
|
||||
if ( S_ISBLK(hostmode) ) mode |= ISO9660_S_IFBLK;
|
||||
if ( S_ISDIR(hostmode) ) mode |= ISO9660_S_IFDIR;
|
||||
if ( S_ISCHR(hostmode) ) mode |= ISO9660_S_IFCHR;
|
||||
if ( S_ISFIFO(hostmode) ) mode |= ISO9660_S_IFIFO;
|
||||
return mode;
|
||||
}
|
||||
|
||||
uint8_t HostDTFromFsDT(uint8_t fsdt)
|
||||
{
|
||||
switch ( fsdt )
|
||||
{
|
||||
case ISO9660_FT_UNKNOWN: return DT_UNKNOWN;
|
||||
case ISO9660_FT_REG_FILE: return DT_REG;
|
||||
case ISO9660_FT_DIR: return DT_DIR;
|
||||
case ISO9660_FT_CHRDEV: return DT_CHR;
|
||||
case ISO9660_FT_BLKDEV: return DT_BLK;
|
||||
case ISO9660_FT_FIFO: return DT_FIFO;
|
||||
case ISO9660_FT_SOCK: return DT_SOCK;
|
||||
case ISO9660_FT_SYMLINK: return DT_LNK;
|
||||
}
|
||||
return DT_UNKNOWN;
|
||||
}
|
||||
|
||||
void StatInode(Inode* inode, struct stat* st)
|
||||
{
|
||||
memset(st, 0, sizeof(*st));
|
||||
st->st_ino = inode->inode_id;
|
||||
st->st_mode = HostModeFromFsMode(inode->Mode());
|
||||
st->st_nlink = inode->nlink;
|
||||
st->st_uid = inode->uid;
|
||||
st->st_gid = inode->gid;
|
||||
st->st_size = inode->size;
|
||||
// TODO: Rock Ridge.
|
||||
st->st_atim.tv_sec = inode->Time();
|
||||
st->st_atim.tv_nsec = 0;
|
||||
// TODO: Rock Ridge.
|
||||
st->st_ctim.tv_sec = inode->Time();
|
||||
st->st_ctim.tv_nsec = 0;
|
||||
// TODO: Rock Ridge.
|
||||
st->st_mtim.tv_sec = inode->Time();
|
||||
st->st_mtim.tv_nsec = 0;
|
||||
st->st_blksize = inode->filesystem->block_size;
|
||||
st->st_blocks = divup(st->st_size, (off_t) 512);
|
||||
}
|
||||
|
||||
static void compact_arguments(int* argc, char*** argv)
|
||||
{
|
||||
for ( int i = 0; i < *argc; i++ )
|
||||
{
|
||||
while ( i < *argc && !(*argv)[i] )
|
||||
{
|
||||
for ( int n = i; n < *argc; n++ )
|
||||
(*argv)[n] = (*argv)[n+1];
|
||||
(*argc)--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void help(FILE* fp, const char* argv0)
|
||||
{
|
||||
fprintf(fp, "Usage: %s [OPTION]... DEVICE [MOUNT-POINT]\n", argv0);
|
||||
}
|
||||
|
||||
static void version(FILE* fp, const char* argv0)
|
||||
{
|
||||
fprintf(fp, "%s (Sortix) %s\n", argv0, VERSIONSTR);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
const char* argv0 = argv[0];
|
||||
const char* pretend_mount_path = NULL;
|
||||
bool foreground = false;
|
||||
for ( int i = 1; i < argc; i++ )
|
||||
{
|
||||
const char* arg = argv[i];
|
||||
if ( arg[0] != '-' || !arg[1] )
|
||||
continue;
|
||||
argv[i] = NULL;
|
||||
if ( !strcmp(arg, "--") )
|
||||
break;
|
||||
if ( arg[1] != '-' )
|
||||
{
|
||||
while ( char c = *++arg ) switch ( c )
|
||||
{
|
||||
case 'r': break;
|
||||
default:
|
||||
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if ( !strcmp(arg, "--help") )
|
||||
help(stdout, argv0), exit(0);
|
||||
else if ( !strcmp(arg, "--version") )
|
||||
version(stdout, argv0), exit(0);
|
||||
else if ( !strcmp(arg, "--background") )
|
||||
foreground = false;
|
||||
else if ( !strcmp(arg, "--foreground") )
|
||||
foreground = true;
|
||||
else if ( !strcmp(arg, "--read") )
|
||||
{
|
||||
}
|
||||
else if ( !strncmp(arg, "--pretend-mount-path=", strlen("--pretend-mount-path=")) )
|
||||
pretend_mount_path = arg + strlen("--pretend-mount-path=");
|
||||
else if ( !strcmp(arg, "--pretend-mount-path") )
|
||||
{
|
||||
if ( i+1 == argc )
|
||||
{
|
||||
fprintf(stderr, "%s: --pretend-mount-path: Missing operand\n", argv0);
|
||||
exit(1);
|
||||
}
|
||||
pretend_mount_path = argv[++i];
|
||||
argv[i] = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if ( argc == 1 )
|
||||
{
|
||||
help(stdout, argv0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
compact_arguments(&argc, &argv);
|
||||
|
||||
const char* device_path = 2 <= argc ? argv[1] : NULL;
|
||||
const char* mount_path = 3 <= argc ? argv[2] : NULL;
|
||||
|
||||
if ( !device_path )
|
||||
{
|
||||
help(stderr, argv0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ( !pretend_mount_path )
|
||||
pretend_mount_path = mount_path;
|
||||
|
||||
int fd = open(device_path, O_RDONLY);
|
||||
if ( fd < 0 )
|
||||
err(1, "%s", device_path);
|
||||
|
||||
// Search for the Primary Volume Descriptor.
|
||||
off_t block_size = 2048;
|
||||
struct iso9660_pvd pvd;
|
||||
off_t pvd_offset;
|
||||
for ( uint32_t i = 16; true; i++ )
|
||||
{
|
||||
pvd_offset = block_size * i;
|
||||
if ( preadall(fd, &pvd, sizeof(pvd), pvd_offset) != sizeof(pvd) )
|
||||
{
|
||||
if ( errno == EEOF )
|
||||
errx(1, "Not a valid ISO 9660 filesystem: %s", device_path);
|
||||
else
|
||||
err(1, "read: %s", device_path);
|
||||
}
|
||||
if ( memcmp(pvd.standard_identifier, "CD001", 5) != 0 )
|
||||
errx(1, "Not a valid ISO 9660 filesystem: %s", device_path);
|
||||
if ( pvd.type == TYPE_PRIMARY_VOLUME_DESCRIPTOR )
|
||||
break;
|
||||
if ( pvd.type == TYPE_VOLUME_DESCRIPTOR_SET_TERMINATOR )
|
||||
errx(1, "Not a valid ISO 9660 filesystem: %s", device_path);
|
||||
}
|
||||
|
||||
if ( pvd.version != 1 || pvd.file_structure_version != 1 )
|
||||
errx(1, "Unsupported ISO 9660 filesystem version: %s", device_path);
|
||||
// TODO: Test if appropriate power of two and allow.
|
||||
if ( le32toh(pvd.logical_block_size_le) != block_size )
|
||||
errx(1, "Unsupported ISO 9660 block size: %s", device_path);
|
||||
block_size = le32toh(pvd.logical_block_size_le);
|
||||
|
||||
Device* dev = new Device(fd, device_path, block_size);
|
||||
if ( !dev )
|
||||
err(1, "malloc");
|
||||
Filesystem* fs = new Filesystem(dev, pretend_mount_path, pvd_offset);
|
||||
if ( !fs )
|
||||
err(1, "%s", device_path);
|
||||
|
||||
// Change the root inode to be its own . entry instead of the pvd record, so
|
||||
// parent directories correctly .. to it, and so Rock Ridge extensions work.
|
||||
Inode* root = fs->GetInode(fs->root_ino);
|
||||
if ( !root )
|
||||
err(1, "%s: GetInode", device_path);
|
||||
fs->root_ino = 0;
|
||||
uint32_t root_lba;
|
||||
uint32_t root_size;
|
||||
memcpy(&root_lba, pvd.root_dirent + 2, sizeof(root_lba));
|
||||
memcpy(&root_size, pvd.root_dirent + 10, sizeof(root_size));
|
||||
root->FindParentInode(root_lba, root_size);
|
||||
fs->root_ino = root->parent_inode_id;
|
||||
root->Unref();
|
||||
|
||||
if ( !mount_path )
|
||||
return 0;
|
||||
|
||||
#if defined(__sortix__)
|
||||
return fsmarshall_main(argv0, mount_path, foreground, fs, dev);
|
||||
#else
|
||||
return iso9660_fuse_main(argv0, mount_path, foreground, fs, dev);
|
||||
#endif
|
||||
}
|
33
iso9660/iso9660fs.h
Normal file
33
iso9660/iso9660fs.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* iso9660fs.h
|
||||
* Implementation of the ISO 9660 filesystem.
|
||||
*/
|
||||
|
||||
#ifndef ISO9660FS_H
|
||||
#define ISO9660FS_H
|
||||
|
||||
extern uid_t request_uid;
|
||||
extern gid_t request_gid;
|
||||
|
||||
class Inode;
|
||||
|
||||
mode_t HostModeFromFsMode(uint32_t fsmode);
|
||||
uint32_t FsModeFromHostMode(mode_t hostmode);
|
||||
uint8_t HostDTFromFsDT(uint8_t fsdt);
|
||||
void StatInode(Inode* inode, struct stat* st);
|
||||
|
||||
#endif
|
49
iso9660/util.h
Normal file
49
iso9660/util.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2013, 2015 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* util.h
|
||||
* Utility functions for the filesystem implementation.
|
||||
*/
|
||||
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
template <class T> T divup(T a, T b)
|
||||
{
|
||||
return a/b + (a % b ? 1 : 0);
|
||||
}
|
||||
|
||||
template <class T> T roundup(T a, T b)
|
||||
{
|
||||
return a % b ? a + b - a % b : a;
|
||||
}
|
||||
|
||||
static inline bool checkbit(const uint8_t* bitmap, size_t bit)
|
||||
{
|
||||
uint8_t bits = bitmap[bit / 8UL];
|
||||
return bits & (1U << (bit % 8UL));
|
||||
}
|
||||
|
||||
static inline void setbit(uint8_t* bitmap, size_t bit)
|
||||
{
|
||||
bitmap[bit / 8UL] |= 1U << (bit % 8UL);
|
||||
}
|
||||
|
||||
static inline void clearbit(uint8_t* bitmap, size_t bit)
|
||||
{
|
||||
bitmap[bit / 8UL] &= ~(1U << (bit % 8UL));
|
||||
}
|
||||
|
||||
#endif
|
|
@ -20,6 +20,7 @@ extended.o \
|
|||
filesystem.o \
|
||||
gpt.o \
|
||||
harddisk.o \
|
||||
iso9660.o \
|
||||
mbr.o \
|
||||
partition.o \
|
||||
uuid.o \
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
#include <mount/ext2.h>
|
||||
#include <mount/extended.h>
|
||||
#include <mount/filesystem.h>
|
||||
#include <mount/iso9660.h>
|
||||
#include <mount/partition.h>
|
||||
|
||||
const char* filesystem_error_string(enum filesystem_error error)
|
||||
|
@ -55,6 +56,7 @@ static const struct filesystem_handler* filesystem_handlers[] =
|
|||
&biosboot_handler,
|
||||
&extended_handler,
|
||||
&ext2_handler,
|
||||
&iso9660_handler,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
|
37
libmount/include/mount/iso9660.h
Normal file
37
libmount/include/mount/iso9660.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* mount/iso9660.h
|
||||
* ISO 9660 filesystem.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_MOUNT_ISO9660_H
|
||||
#define INCLUDE_MOUNT_ISO9660_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <mount/filesystem.h>
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const struct filesystem_handler iso9660_handler;
|
||||
|
||||
#if defined(__cplusplus)
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
98
libmount/iso9660.c
Normal file
98
libmount/iso9660.c
Normal file
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Jonas 'Sortie' Termansen.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* iso9660.c
|
||||
* iso9660 filesystem.
|
||||
*/
|
||||
|
||||
#include <endian.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <mount/blockdevice.h>
|
||||
#include <mount/filesystem.h>
|
||||
#include <mount/iso9660.h>
|
||||
#include <mount/uuid.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
static size_t iso9660_probe_amount(struct blockdevice* bdev)
|
||||
{
|
||||
(void) bdev;
|
||||
size_t block_size = 2048 /* TODO: Actual block size? */;
|
||||
return 17 * block_size;
|
||||
}
|
||||
|
||||
static bool iso9660_probe(struct blockdevice* bdev,
|
||||
const unsigned char* leading,
|
||||
size_t amount)
|
||||
{
|
||||
(void) bdev;
|
||||
size_t block_size = 2048 /* TODO: Actual block size? */;
|
||||
if ( amount < 17 * block_size )
|
||||
return false;
|
||||
size_t offset = 16 * block_size;
|
||||
return !memcmp(leading + offset + 1, "CD001", 5);
|
||||
}
|
||||
|
||||
static void iso9660_release(struct filesystem* fs)
|
||||
{
|
||||
if ( !fs )
|
||||
return;
|
||||
free(fs);
|
||||
}
|
||||
|
||||
static enum filesystem_error iso9660_inspect(struct filesystem** fs_ptr,
|
||||
struct blockdevice* bdev)
|
||||
{
|
||||
*fs_ptr = NULL;
|
||||
struct filesystem* fs = CALLOC_TYPE(struct filesystem);
|
||||
if ( !fs )
|
||||
return FILESYSTEM_ERROR_ERRNO;
|
||||
fs->bdev = bdev;
|
||||
fs->handler = &iso9660_handler;
|
||||
fs->handler_private = NULL;
|
||||
fs->fstype_name = "iso9660";
|
||||
fs->driver = "iso9660fs";
|
||||
size_t block_size = 2048 /* TODO: Actual block size? */;
|
||||
size_t offset = 16 * block_size;
|
||||
uint8_t pvd[2048];
|
||||
if ( blockdevice_preadall(bdev, pvd, sizeof(pvd), offset) != sizeof(pvd) )
|
||||
return iso9660_release(fs), FILESYSTEM_ERROR_ERRNO;
|
||||
// TODO: LABEL support using the volume name.
|
||||
char vol_set[128 + 1];
|
||||
memcpy(vol_set, pvd + 190, 128);
|
||||
vol_set[128] = '\0';
|
||||
for ( size_t i = 128; i && vol_set[i-1] == ' '; i-- )
|
||||
vol_set[i-1] = '\0';
|
||||
if ( uuid_validate(vol_set) )
|
||||
{
|
||||
fs->flags |= FILESYSTEM_FLAG_UUID;
|
||||
uuid_from_string(fs->uuid, vol_set);
|
||||
}
|
||||
return *fs_ptr = fs, FILESYSTEM_ERROR_NONE;
|
||||
}
|
||||
|
||||
const struct filesystem_handler iso9660_handler =
|
||||
{
|
||||
.handler_name = "iso9660",
|
||||
.probe_amount = iso9660_probe_amount,
|
||||
.probe = iso9660_probe,
|
||||
.inspect = iso9660_inspect,
|
||||
.release = iso9660_release,
|
||||
};
|
|
@ -163,6 +163,9 @@ bool blockdevice_probe_partition_table_type(enum partition_table_type* result_ou
|
|||
memcpy(&mbr, leading, sizeof(mbr));
|
||||
if ( mbr.signature[0] != 0x55 && mbr.signature[1] != 0xAA )
|
||||
break;
|
||||
// Ignore the partition table on live cdrom images.
|
||||
if ( mbr.partitions[0][4 /*system_id*/] == 0xCD )
|
||||
break;
|
||||
result = PARTITION_TABLE_TYPE_MBR;
|
||||
goto out;
|
||||
} while ( 0 );
|
||||
|
|
|
@ -127,6 +127,9 @@ ext2 extensions.
|
|||
You can make a compatible filesystem with:
|
||||
.Pp
|
||||
.Dl $ mkfs.ext2 -O none,large_file,filetype
|
||||
.Pp
|
||||
You can mount ISO 9660 filesystems for e.g. CD-ROMs using
|
||||
.Xr iso9660fs 8 .
|
||||
.Ss Networking
|
||||
Internet Protocol version 4
|
||||
.Pq Xr ip 4
|
||||
|
|
|
@ -157,6 +157,12 @@ static bool should_install_bootloader_path(const char* mnt,
|
|||
return result;
|
||||
}
|
||||
|
||||
static bool should_ignore_bootloader_on_filesystem(struct blockdevice* bdev)
|
||||
{
|
||||
return bdev->fs && bdev->fs->driver &&
|
||||
!strcmp(bdev->fs->driver, "iso9660fs");
|
||||
}
|
||||
|
||||
static bool should_install_bootloader_bdev(struct blockdevice* bdev)
|
||||
{
|
||||
if ( !bdev->fs )
|
||||
|
@ -196,13 +202,16 @@ static bool should_install_bootloader(void)
|
|||
{
|
||||
for ( size_t n = 0; n < hd->bdev.pt->partitions_count; n++ )
|
||||
{
|
||||
any_systems = true;
|
||||
struct partition* p = hd->bdev.pt->partitions[n];
|
||||
if ( should_ignore_bootloader_on_filesystem(&p->bdev) )
|
||||
continue;
|
||||
any_systems = true;
|
||||
if ( should_install_bootloader_bdev(&p->bdev) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if ( hd->bdev.fs )
|
||||
else if ( hd->bdev.fs &&
|
||||
!should_ignore_bootloader_on_filesystem(&hd->bdev) )
|
||||
{
|
||||
any_systems = true;
|
||||
if ( should_install_bootloader_bdev(&hd->bdev) )
|
||||
|
|
|
@ -80,6 +80,7 @@ struct command commands[] =
|
|||
{LOGOUT, "logout", NULL, suggest_logout},
|
||||
|
||||
{MOUNT, "extfs", "system", NULL},
|
||||
{MOUNT, "iso9660fs", "system", NULL},
|
||||
{MOUNT, "mount", NULL, NULL},
|
||||
|
||||
{PAGER, "less", NULL, NULL},
|
||||
|
|
|
@ -33,4 +33,5 @@ It is not safe to shut down the computer directly after using this option.
|
|||
will exit 0 on success and non-zero otherwise.
|
||||
.Sh SEE ALSO
|
||||
.Xr unmount 2 ,
|
||||
.Xr extfs 8
|
||||
.Xr extfs 8 ,
|
||||
.Xr iso9660fs 8
|
||||
|
|
Loading…
Reference in a new issue