858 lines
23 KiB
C++
858 lines
23 KiB
C++
/*
|
|
* 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 "fat.h"
|
|
#include "fatfs.h"
|
|
#include "filesystem.h"
|
|
#include "fsmarshall.h"
|
|
#include "fuse.h"
|
|
#include "inode.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;
|
|
//fprintf(stderr, "fatfs: sending error %i (%s)\n", errnum, strerror(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 ( (uint32_t) ino != ino )
|
|
return errno = EBADF, (Inode*) NULL;
|
|
return fs->GetInode((uint32_t) ino);
|
|
}
|
|
|
|
void HandleRefer(int chl, struct fsm_req_refer* msg, Filesystem* fs)
|
|
{
|
|
(void) chl;
|
|
if ( Inode* inode = SafeGetInode(fs, (uint32_t) msg->ino) )
|
|
{
|
|
if ( inode->implied_reference )
|
|
inode->implied_reference--;
|
|
else
|
|
inode->RemoteRefer();
|
|
inode->Unref();
|
|
}
|
|
}
|
|
|
|
void HandleUnref(int chl, struct fsm_req_unref* msg, Filesystem* fs)
|
|
{
|
|
(void) chl;
|
|
if ( Inode* inode = SafeGetInode(fs, (uint32_t) 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->Sync();
|
|
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)
|
|
{
|
|
if ( !fs->device->write ) { RespondError(chl, EROFS); return; }
|
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
|
if ( !inode ) { RespondError(chl, errno); return; }
|
|
if ( !inode->ChangeMode(msg->mode) )
|
|
RespondError(chl, errno);
|
|
else
|
|
RespondSuccess(chl);
|
|
inode->Unref();
|
|
}
|
|
|
|
void HandleChangeOwner(int chl, struct fsm_req_chown* msg, Filesystem* fs)
|
|
{
|
|
if ( !fs->device->write ) { RespondError(chl, EROFS); return; }
|
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
|
if ( !inode ) { RespondError(chl, errno); return; }
|
|
if ( !inode->ChangeOwner(msg->uid, msg->gid) )
|
|
RespondError(chl, errno);
|
|
else
|
|
RespondSuccess(chl);
|
|
inode->Unref();
|
|
}
|
|
|
|
void HandleUTimens(int chl, struct fsm_req_utimens* msg, Filesystem* fs)
|
|
{
|
|
if ( !fs->device->write ) { RespondError(chl, EROFS); return; }
|
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
|
inode->UTimens(msg->times);
|
|
inode->Unref();
|
|
RespondSuccess(chl);
|
|
}
|
|
|
|
void HandleTruncate(int chl, struct fsm_req_truncate* msg, Filesystem* fs)
|
|
{
|
|
if ( !fs->device->write ) { RespondError(chl, EROFS); return; }
|
|
if ( msg->size < 0 ) { RespondError(chl, EINVAL); return; }
|
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
|
if ( !inode ) { RespondError(chl, errno); return; }
|
|
inode->Truncate((uint64_t) msg->size);
|
|
inode->Unref();
|
|
RespondSuccess(chl);
|
|
}
|
|
|
|
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)
|
|
{
|
|
Inode* inode = SafeGetInode(fs, msg->ino);
|
|
if ( !inode ) { RespondError(chl, errno); return; }
|
|
const uint8_t* buf = (const uint8_t*) &msg[1];
|
|
ssize_t amount = inode->WriteAt(buf, msg->count, msg->offset);
|
|
inode->Unref();
|
|
if ( amount < 0 ) { RespondError(chl, errno); return; }
|
|
RespondWrite(chl, amount);
|
|
}
|
|
|
|
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, msg->mode & 07777);
|
|
|
|
free(path);
|
|
inode->Unref();
|
|
|
|
if ( !result ) { RespondError(chl, errno); return; }
|
|
|
|
RespondOpen(chl, result->inode_id, result->Mode() & S_IFMT);
|
|
// TODO: Unfortunately Open does not implicitly imply RemoteRefer so we need
|
|
// to try and pretend that it does so the inode isn't destroyed early.
|
|
result->implied_reference++;
|
|
result->RemoteRefer();
|
|
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; }
|
|
|
|
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->CreateDirectory(path, msg->mode & 07777);
|
|
|
|
free(path);
|
|
inode->Unref();
|
|
|
|
if ( !result ) { RespondError(chl, errno); return; }
|
|
|
|
RespondMakeDir(chl, result->inode_id);
|
|
result->Unref();
|
|
}
|
|
|
|
// TODO: Encapsulate.
|
|
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;
|
|
// TODO: Adjust with LFN's real limit.
|
|
uint8_t padding[sizeof(struct dirent) + 256];
|
|
};
|
|
memset(&kernel_entry, 0, sizeof(kernel_entry));
|
|
|
|
if ( inode->inode_id == inode->filesystem->root_inode_id )
|
|
{
|
|
if ( msg->rec_num < 2 )
|
|
{
|
|
const char* name = msg->rec_num ? ".." : ".";
|
|
size_t name_len = strlen(name);
|
|
kernel_entry.d_reclen = sizeof(kernel_entry) + name_len;
|
|
kernel_entry.d_ino = inode->inode_id;
|
|
kernel_entry.d_dev = 0;
|
|
kernel_entry.d_type = DT_DIR;
|
|
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';
|
|
RespondReadDir(chl, &kernel_entry);
|
|
return;
|
|
}
|
|
msg->rec_num -= 2;
|
|
}
|
|
|
|
uint32_t cluster = inode->first_cluster;
|
|
uint8_t sector = 0;
|
|
uint16_t offset = 0;
|
|
Block* block = NULL;
|
|
while ( inode->Iterate(&block, &cluster, §or, &offset) )
|
|
{
|
|
const uint8_t* block_data = block->block_data + offset;
|
|
const struct fat_dirent* entry = (const struct fat_dirent*) block_data;
|
|
if ( !entry->name[0] )
|
|
break;
|
|
if ( (unsigned char) entry->name[0] != 0xE5 &&
|
|
!(entry->attributes & FAT_ATTRIBUTE_VOLUME_ID) &&
|
|
!(msg->rec_num--) )
|
|
{
|
|
char name[8 + 1 + 3 + 1];
|
|
decode_8_3(entry->name, name);
|
|
size_t name_len = strnlen(entry->name, sizeof(entry->name));
|
|
uint8_t file_type =
|
|
entry->attributes & FAT_ATTRIBUTE_DIRECTORY ? DT_DIR : DT_REG;
|
|
kernel_entry.d_reclen = sizeof(kernel_entry) + name_len;
|
|
kernel_entry.d_ino = entry->cluster_low | entry->cluster_high << 16;
|
|
kernel_entry.d_dev = 0;
|
|
kernel_entry.d_type = 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;
|
|
}
|
|
offset += sizeof(struct fat_dirent);
|
|
}
|
|
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, false);
|
|
|
|
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; }
|
|
|
|
char* dest_raw = (char*) &(msg[1]);
|
|
char* dest = (char*) malloc(msg->targetlen + 1);
|
|
if ( !dest )
|
|
{
|
|
RespondError(chl, errno);
|
|
inode->Unref();
|
|
return;
|
|
}
|
|
memcpy(dest, dest_raw, msg->targetlen);
|
|
dest[msg->targetlen] = '\0';
|
|
|
|
char* path_raw = (char*) dest_raw + msg->targetlen;
|
|
char* path = (char*) malloc(msg->namelen + 1);
|
|
if ( !path )
|
|
{
|
|
free(dest);
|
|
RespondError(chl, errno);
|
|
inode->Unref();
|
|
return;
|
|
}
|
|
memcpy(path, path_raw, msg->namelen);
|
|
path[msg->namelen] = '\0';
|
|
|
|
bool result = inode->Symlink(path, dest);
|
|
|
|
free(path);
|
|
free(dest);
|
|
inode->Unref();
|
|
|
|
if ( !result ) { RespondError(chl, errno); return; }
|
|
|
|
RespondSuccess(chl);
|
|
}
|
|
|
|
void HandleReadlink(int chl, struct fsm_req_readlink* msg, Filesystem* fs)
|
|
{
|
|
(void) msg;
|
|
(void) fs;
|
|
RespondError(chl, EINVAL);
|
|
}
|
|
|
|
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->cluster_size;
|
|
stvfs.f_frsize = fs->cluster_size;
|
|
stvfs.f_blocks = fs->cluster_count;
|
|
// TODO: Locate FsInfo and count on FAT12/FAT16.
|
|
stvfs.f_bfree = fs->CalculateFreeCount();
|
|
stvfs.f_bavail = stvfs.f_bfree;
|
|
stvfs.f_files = stvfs.f_blocks;
|
|
stvfs.f_ffree = stvfs.f_bfree;
|
|
stvfs.f_favail = stvfs.f_files;
|
|
stvfs.f_fsid = 0;
|
|
stvfs.f_flag = 0;
|
|
if ( !fs->device->write )
|
|
stvfs.f_flag |= ST_RDONLY;
|
|
stvfs.f_namemax = 8 + 3; // TODO: Long file name support.
|
|
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"
|
|
"fat-size\0volume-id\0volume-label\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, "fat", strlen("fat"));
|
|
else if ( !strcmp(name, "fat-size") )
|
|
{
|
|
const char* str = fs->fat_type == 32 ? "32" :
|
|
fs->fat_type == 16 ? "16" : "12";
|
|
RespondTCGetBlob(chl, str, strlen(str));
|
|
}
|
|
else if ( !strcmp(name, "filesystem-uuid") )
|
|
{
|
|
unsigned char uuid[16];
|
|
if ( fs->fat_type == 32 )
|
|
{
|
|
memcpy(uuid, &fs->bpb->fat32_volume_id, 4);
|
|
memcpy(uuid + 4, &fs->bpb->fat32_volume_label, 11);
|
|
}
|
|
else
|
|
{
|
|
memcpy(uuid, &fs->bpb->fat12_volume_id, 4);
|
|
memcpy(uuid + 4, &fs->bpb->fat12_volume_label, 11);
|
|
}
|
|
uuid[15] = '\0';
|
|
RespondTCGetBlob(chl, uuid, sizeof(uuid));
|
|
}
|
|
else if ( !strcmp(name, "volume-id") )
|
|
{
|
|
if ( fs->fat_type == 32 )
|
|
RespondTCGetBlob(chl, &fs->bpb->fat32_volume_id, 4);
|
|
else
|
|
RespondTCGetBlob(chl, &fs->bpb->fat12_volume_id, 4);
|
|
}
|
|
else if ( !strcmp(name, "volume-label") )
|
|
{
|
|
if ( fs->fat_type == 32 )
|
|
RespondTCGetBlob(chl, &fs->bpb->fat32_volume_label, 11);
|
|
else
|
|
RespondTCGetBlob(chl, &fs->bpb->fat12_volume_label, 11);
|
|
}
|
|
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;
|
|
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] )
|
|
{
|
|
warn("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 directory;
|
|
struct stat root_dir_st;
|
|
memset(&root_dir_st, 0, sizeof(root_dir_st));
|
|
root_dir_st.st_ino = fs->root_inode_id;
|
|
root_dir_st.st_mode = S_IFDIR | 0755;
|
|
|
|
// Create a filesystem server connected to the kernel that we'll listen on.
|
|
int serverfd = fsm_mountat(AT_FDCWD, mount_path, &root_dir_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();
|
|
|
|
dev->SpawnSyncThread();
|
|
|
|
// Listen for filesystem messages and sync the filesystem every few seconds.
|
|
struct timespec last_sync_at;
|
|
clock_gettime(CLOCK_MONOTONIC, &last_sync_at);
|
|
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);
|
|
|
|
if ( dev->write && !dev->has_sync_thread )
|
|
{
|
|
struct timespec now;
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
if ( 5 <= timespec_sub(now, last_sync_at).tv_sec )
|
|
{
|
|
fs->Sync();
|
|
last_sync_at = now;
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: Replace with FAT concept.
|
|
// 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();
|
|
}
|
|
|
|
// Sync the filesystem before shutting down.
|
|
if ( dev->write )
|
|
fs->Sync();
|
|
|
|
close(serverfd);
|
|
|
|
delete fs;
|
|
delete dev;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|