libc: add support for 'ftruncate()'

Fixes #307.
This commit is contained in:
Christian Prochaska 2012-08-02 15:17:24 +02:00 committed by Norman Feske
parent 280fc59edf
commit dcfcbce856
22 changed files with 283 additions and 94 deletions

View File

@ -77,6 +77,7 @@ namespace Libc {
virtual void freeaddrinfo(struct ::addrinfo *res);
virtual int fstat(File_descriptor *, struct stat *buf);
virtual int fsync(File_descriptor *);
virtual int ftruncate(File_descriptor *, ::off_t length);
virtual int getaddrinfo(const char *node, const char *service,
const struct ::addrinfo *hints,
struct ::addrinfo **res);

View File

@ -50,7 +50,6 @@ DUMMY(-1, _fpathconf)
DUMMY(-1, fpathconf)
DUMMY(-1, freebsd7___semctl)
DUMMY(-1, fstatat)
DUMMY(-1, ftruncate)
DUMMY(-1, getcontext)
DUMMY( 0, __getcwd)
DUMMY( 0, getdtablesize)

View File

@ -219,6 +219,10 @@ extern "C" int fsync(int libc_fd) {
FD_FUNC_WRAPPER(fsync, libc_fd); }
extern "C" int ftruncate(int libc_fd, ::off_t length) {
FD_FUNC_WRAPPER(ftruncate, libc_fd, length); }
extern "C" int getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints,
struct addrinfo **res)

View File

@ -154,6 +154,7 @@ DUMMY(int, -1, fchdir, (File_descriptor *));
DUMMY(int, -1, fcntl, (File_descriptor *, int cmd, long arg));
DUMMY(int, -1, fstat, (File_descriptor *, struct stat *));
DUMMY(int, -1, fsync, (File_descriptor *));
DUMMY(int, -1, ftruncate, (File_descriptor *, ::off_t));
DUMMY(ssize_t, -1, getdirentries, (File_descriptor *, char *, ::size_t, ::off_t *));
DUMMY(int, -1, getpeername, (File_descriptor *, struct sockaddr *, socklen_t *));
DUMMY(int, -1, getsockname, (File_descriptor *, struct sockaddr *, socklen_t *));

View File

@ -312,6 +312,33 @@ class Plugin : public Libc::Plugin
}
}
int ftruncate(Libc::File_descriptor *fd, ::off_t length)
{
using namespace Ffat;
/* 'f_truncate()' truncates to the current seek pointer */
if (lseek(fd, length, SEEK_SET) == -1)
return -1;
FRESULT res = f_truncate(_get_ffat_file(fd));
switch(res) {
case FR_OK:
return 0;
case FR_DISK_ERR:
case FR_INT_ERR:
case FR_NOT_READY:
case FR_INVALID_OBJECT:
errno = EIO;
return -1;
default:
/* not supposed to occur according to the libffat documentation */
PERR("f_truncate() returned an unexpected error code");
return -1;
}
}
ssize_t getdirentries(Libc::File_descriptor *fd, char *buf,
::size_t nbytes, ::off_t *basep)
{

View File

@ -356,6 +356,25 @@ class Plugin : public Libc::Plugin
return -1;
}
int ftruncate(Libc::File_descriptor *fd, ::off_t length)
{
File_system::Node_handle node_handle = context(fd)->node_handle();
File_system::File_handle &file_handle =
static_cast<File_system::File_handle&>(node_handle);
try {
file_system()->truncate(file_handle, length);
} catch (File_system::Invalid_handle) {
errno = EINVAL;
return -1;
} catch (File_system::Permission_denied) {
errno = EPERM;
return -1;
}
return 0;
}
/*
* *basep does not get initialized by the libc and is therefore
* useless for determining a specific directory index. This

View File

@ -31,6 +31,13 @@ namespace Ffat { extern "C" {
#include <ffat/ff.h>
} }
/*
* This macro is defined in later versions of the FatFs lib, but not in the
* one currently used for Genode.
*/
#define f_tell(fp) ((fp)->fptr)
using namespace Genode;
@ -767,14 +774,44 @@ namespace File_system {
using namespace Ffat;
FRESULT res = f_truncate(file->ffat_fil());
/* 'f_truncate()' truncates to the current seek pointer */
FRESULT res = f_lseek(file->ffat_fil(), size);
switch(res) {
case FR_OK:
/* according to the FatFs documentation this can happen */
if (f_tell(file->ffat_fil()) != size) {
PERR("f_lseek() could not seek to offset %llu", size);
return;
}
break;
case FR_DISK_ERR:
PERR("f_lseek() failed with error code FR_DISK_ERR");
return;
case FR_INT_ERR:
PERR("f_lseek() failed with error code FR_INT_ERR");
return;
case FR_NOT_READY:
PERR("f_lseek() failed with error code FR_NOT_READY");
return;
case FR_INVALID_OBJECT:
PERR("f_lseek() failed with error code FR_INVALID_OBJECT");
throw Invalid_handle();
default:
/* not supposed to occur according to the libffat documentation */
PERR("f_lseek() returned an unexpected error code");
return;
}
res = f_truncate(file->ffat_fil());
switch(res) {
case FR_OK:
return;
case FR_INVALID_OBJECT:
PERR("f_truncate() failed with error code FR_INVALID_OBJECT");
return;
throw Invalid_handle();
case FR_DISK_ERR:
PERR("f_truncate() failed with error code FR_DISK_ERR");
return;

View File

@ -47,6 +47,7 @@ int main(int argc, char *argv[])
char const *file_name = "test.tst";
char const *file_name2 = "test2.tst";
char const *file_name3 = "test3.tst";
char const *file_name4 = "test4.tst";
char const *pattern = "a single line of text";
size_t pattern_size = strlen(pattern) + 1;
@ -165,6 +166,20 @@ int main(int argc, char *argv[])
}
}
/* test 'ftruncate()' */
CALL_AND_CHECK(fd, open(file_name4, O_CREAT | O_WRONLY), fd >= 0, "file_name=%s", file_name4);
CALL_AND_CHECK(ret, ftruncate(fd, 100), ret == 0, ""); /* increase size */
CALL_AND_CHECK(ret, close(fd), ret == 0, "");
CALL_AND_CHECK(ret, stat(file_name4, &stat_buf),
(ret == 0) && (stat_buf.st_size == 100),
"file_name=%s", file_name4);
CALL_AND_CHECK(fd, open(file_name4, O_WRONLY), fd >= 0, "file_name=%s", file_name4);
CALL_AND_CHECK(ret, ftruncate(fd, 10), ret == 0, ""); /* decrease size */
CALL_AND_CHECK(ret, close(fd), ret == 0, "");
CALL_AND_CHECK(ret, stat(file_name4, &stat_buf),
(ret == 0) && (stat_buf.st_size == 10),
"file_name=%s", file_name4);
if (i < (iterations - 1))
sleep(2);
}

View File

@ -88,6 +88,14 @@ namespace File_system {
}
file_size_t length() const { return _length; }
void truncate(file_size_t size)
{
if (size < _chunk.used_size())
_chunk.truncate(size);
else
_length = size;
}
};
}

View File

@ -373,7 +373,15 @@ namespace File_system {
destroy(env()->heap(), node);
}
void truncate(File_handle, file_size_t size) { }
void truncate(File_handle file_handle, file_size_t size)
{
if (!_writable)
throw Permission_denied();
File *file = _handle_registry.lookup_and_lock(file_handle);
Node_lock_guard file_guard(*file);
file->truncate(size);
}
void move(Dir_handle from_dir_handle, Name const &from_name,
Dir_handle to_dir_handle, Name const &to_name)

View File

@ -40,6 +40,7 @@ namespace Noux {
SYSCALL_STAT,
SYSCALL_LSTAT,
SYSCALL_FSTAT,
SYSCALL_FTRUNCATE,
SYSCALL_FCNTL,
SYSCALL_OPEN,
SYSCALL_CLOSE,
@ -83,6 +84,7 @@ namespace Noux {
NOUX_DECL_SYSCALL_NAME(STAT)
NOUX_DECL_SYSCALL_NAME(LSTAT)
NOUX_DECL_SYSCALL_NAME(FSTAT)
NOUX_DECL_SYSCALL_NAME(FTRUNCATE)
NOUX_DECL_SYSCALL_NAME(FCNTL)
NOUX_DECL_SYSCALL_NAME(OPEN)
NOUX_DECL_SYSCALL_NAME(CLOSE)

View File

@ -271,126 +271,130 @@ namespace Noux {
char ai_canonname[255];
};
enum General_error { ERR_FD_INVALID, NUM_GENERAL_ERRORS };
enum Stat_error { STAT_ERR_NO_ENTRY = NUM_GENERAL_ERRORS };
enum Fchdir_error { FCHDIR_ERR_NOT_DIR = NUM_GENERAL_ERRORS };
enum Fcntl_error { FCNTL_ERR_CMD_INVALID = NUM_GENERAL_ERRORS };
enum Open_error { OPEN_ERR_UNACCESSIBLE, OPEN_ERR_NO_PERM,
OPEN_ERR_EXISTS };
enum Execve_error { EXECVE_NONEXISTENT = NUM_GENERAL_ERRORS };
enum Unlink_error { UNLINK_ERR_NO_ENTRY, UNLINK_ERR_NO_PERM };
enum Rename_error { RENAME_ERR_NO_ENTRY, RENAME_ERR_CROSS_FS,
RENAME_ERR_NO_PERM };
enum Mkdir_error { MKDIR_ERR_EXISTS, MKDIR_ERR_NO_ENTRY,
MKDIR_ERR_NO_SPACE, MKDIR_ERR_NO_PERM,
MKDIR_ERR_NAME_TOO_LONG};
enum General_error { ERR_FD_INVALID, NUM_GENERAL_ERRORS };
enum Stat_error { STAT_ERR_NO_ENTRY = NUM_GENERAL_ERRORS };
enum Fchdir_error { FCHDIR_ERR_NOT_DIR = NUM_GENERAL_ERRORS };
enum Fcntl_error { FCNTL_ERR_CMD_INVALID = NUM_GENERAL_ERRORS };
enum Ftruncate_error { FTRUNCATE_ERR_NO_PERM = NUM_GENERAL_ERRORS };
enum Open_error { OPEN_ERR_UNACCESSIBLE, OPEN_ERR_NO_PERM,
OPEN_ERR_EXISTS };
enum Execve_error { EXECVE_NONEXISTENT = NUM_GENERAL_ERRORS };
enum Unlink_error { UNLINK_ERR_NO_ENTRY, UNLINK_ERR_NO_PERM };
enum Rename_error { RENAME_ERR_NO_ENTRY, RENAME_ERR_CROSS_FS,
RENAME_ERR_NO_PERM };
enum Mkdir_error { MKDIR_ERR_EXISTS, MKDIR_ERR_NO_ENTRY,
MKDIR_ERR_NO_SPACE, MKDIR_ERR_NO_PERM,
MKDIR_ERR_NAME_TOO_LONG};
union {
General_error general;
Stat_error stat;
Fchdir_error fchdir;
Fcntl_error fcntl;
Open_error open;
Execve_error execve;
Unlink_error unlink;
Rename_error rename;
Mkdir_error mkdir;
General_error general;
Stat_error stat;
Fchdir_error fchdir;
Fcntl_error fcntl;
Ftruncate_error ftruncate;
Open_error open;
Execve_error execve;
Unlink_error unlink;
Rename_error rename;
Mkdir_error mkdir;
} error;
union {
SYSIO_DECL(getcwd, { }, { Path path; });
SYSIO_DECL(getcwd, { }, { Path path; });
SYSIO_DECL(write, { int fd; size_t count; Chunk chunk; },
{ size_t count; });
SYSIO_DECL(write, { int fd; size_t count; Chunk chunk; },
{ size_t count; });
SYSIO_DECL(stat, { Path path; }, { Stat st; });
SYSIO_DECL(stat, { Path path; }, { Stat st; });
SYSIO_DECL(fstat, { int fd; }, { Stat st; });
SYSIO_DECL(fstat, { int fd; }, { Stat st; });
SYSIO_DECL(fcntl, { int fd; long long_arg; Fcntl_cmd cmd; },
{ int result; });
SYSIO_DECL(ftruncate, { int fd; off_t length; }, { });
SYSIO_DECL(open, { Path path; int mode; }, { int fd; });
SYSIO_DECL(fcntl, { int fd; long long_arg; Fcntl_cmd cmd; },
{ int result; });
SYSIO_DECL(close, { int fd; }, { });
SYSIO_DECL(open, { Path path; int mode; }, { int fd; });
SYSIO_DECL(ioctl, : Ioctl_in { int fd; }, : Ioctl_out { });
SYSIO_DECL(close, { int fd; }, { });
SYSIO_DECL(lseek, { int fd; off_t offset; Lseek_whence whence; },
{ off_t offset; });
SYSIO_DECL(ioctl, : Ioctl_in { int fd; }, : Ioctl_out { });
SYSIO_DECL(dirent, { int fd; }, { Dirent entry; });
SYSIO_DECL(lseek, { int fd; off_t offset; Lseek_whence whence; },
{ off_t offset; });
SYSIO_DECL(fchdir, { int fd; }, { });
SYSIO_DECL(dirent, { int fd; }, { Dirent entry; });
SYSIO_DECL(read, { int fd; size_t count; },
{ Chunk chunk; size_t count; });
SYSIO_DECL(fchdir, { int fd; }, { });
SYSIO_DECL(execve, { Path filename; Args args; Env env; }, { });
SYSIO_DECL(read, { int fd; size_t count; },
{ Chunk chunk; size_t count; });
SYSIO_DECL(select, { Select_fds fds; Select_timeout timeout; },
{ Select_fds fds; });
SYSIO_DECL(execve, { Path filename; Args args; Env env; }, { });
SYSIO_DECL(fork, { addr_t ip; addr_t sp;
addr_t parent_cap_addr; },
{ int pid; });
SYSIO_DECL(select, { Select_fds fds; Select_timeout timeout; },
{ Select_fds fds; });
SYSIO_DECL(getpid, { }, { int pid; });
SYSIO_DECL(fork, { addr_t ip; addr_t sp;
addr_t parent_cap_addr; },
{ int pid; });
SYSIO_DECL(wait4, { int pid; bool nohang; },
{ int pid; int status; });
SYSIO_DECL(pipe, { }, { int fd[2]; });
SYSIO_DECL(getpid, { }, { int pid; });
SYSIO_DECL(dup2, { int fd; int to_fd; }, { });
SYSIO_DECL(wait4, { int pid; bool nohang; },
{ int pid; int status; });
SYSIO_DECL(pipe, { }, { int fd[2]; });
SYSIO_DECL(unlink, { Path path; }, { });
SYSIO_DECL(dup2, { int fd; int to_fd; }, { });
SYSIO_DECL(rename, { Path from_path; Path to_path; }, { });
SYSIO_DECL(unlink, { Path path; }, { });
SYSIO_DECL(mkdir, { Path path; int mode; }, { });
SYSIO_DECL(rename, { Path from_path; Path to_path; }, { });
SYSIO_DECL(socket, { int domain; int type; int protocol; },
{ int fd; });
SYSIO_DECL(mkdir, { Path path; int mode; }, { });
SYSIO_DECL(socket, { int domain; int type; int protocol; },
{ int fd; });
/* XXX for now abuse Chunk for passing optval */
SYSIO_DECL(getsockopt, { int fd; int level; int optname; Chunk optval;
socklen_t optlen; }, { int result; });
SYSIO_DECL(getsockopt, { int fd; int level; int optname; Chunk optval;
socklen_t optlen; }, { int result; });
SYSIO_DECL(setsockopt, { int fd; int level;
int optname; Chunk optval;
socklen_t optlen; }, { });
SYSIO_DECL(setsockopt, { int fd; int level;
int optname; Chunk optval;
socklen_t optlen; }, { });
SYSIO_DECL(accept, { int fd; struct sockaddr addr; socklen_t addrlen; },
{ int fd; });
SYSIO_DECL(accept, { int fd; struct sockaddr addr; socklen_t addrlen; },
{ int fd; });
SYSIO_DECL(bind, { int fd; struct sockaddr addr;
socklen_t addrlen; }, { int result; });
SYSIO_DECL(bind, { int fd; struct sockaddr addr;
socklen_t addrlen; }, { int result; });
SYSIO_DECL(getpeername, { int fd; struct sockaddr addr;
socklen_t addrlen; }, { });
SYSIO_DECL(listen, { int fd; int type; int backlog; },
{ int result; });
SYSIO_DECL(listen, { int fd; int type; int backlog; },
{ int result; });
SYSIO_DECL(send, { int fd; Chunk buf; size_t len; int flags; },
{ ssize_t len; });
SYSIO_DECL(send, { int fd; Chunk buf; size_t len; int flags; },
{ ssize_t len; });
SYSIO_DECL(sendto, { int fd; Chunk buf; size_t len; int flags;
struct sockaddr dest_addr; socklen_t addrlen; },
{ ssize_t len; });
SYSIO_DECL(sendto, { int fd; Chunk buf; size_t len; int flags;
struct sockaddr dest_addr; socklen_t addrlen; },
{ ssize_t len; });
SYSIO_DECL(recv, { int fd; Chunk buf; size_t len; int flags; },
{ size_t len; });
SYSIO_DECL(recv, { int fd; Chunk buf; size_t len; int flags; },
{ size_t len; });
SYSIO_DECL(recvfrom, { int fd; Chunk buf; size_t len; int flags;
struct sockaddr src_addr; socklen_t addrlen; },
{ size_t len; });
SYSIO_DECL(recvfrom, { int fd; Chunk buf; size_t len; int flags;
struct sockaddr src_addr; socklen_t addrlen; },
{ size_t len; });
SYSIO_DECL(shutdown, { int fd; int how; }, { });
SYSIO_DECL(shutdown, { int fd; int how; }, { });
SYSIO_DECL(connect, { int fd; struct sockaddr addr; socklen_t addrlen; },
{ int result; });
SYSIO_DECL(connect, { int fd; struct sockaddr addr; socklen_t addrlen; },
{ int result; });
SYSIO_DECL(getaddrinfo, { Hostname hostname; Servname servname;
Addrinfo hints;

View File

@ -552,6 +552,7 @@ namespace {
int fstat(Libc::File_descriptor *, struct stat *);
int fsync(Libc::File_descriptor *);
int fstatfs(Libc::File_descriptor *, struct statfs *);
int ftruncate(Libc::File_descriptor *, ::off_t);
int fcntl(Libc::File_descriptor *, int, long);
ssize_t getdirentries(Libc::File_descriptor *, char *, ::size_t, ::off_t *);
::off_t lseek(Libc::File_descriptor *, ::off_t offset, int whence);
@ -855,6 +856,21 @@ namespace {
}
int Plugin::ftruncate(Libc::File_descriptor *fd, ::off_t length)
{
sysio()->ftruncate_in.fd = noux_fd(fd->context);
sysio()->ftruncate_in.length = length;
if (!noux()->syscall(Noux::Session::SYSCALL_FTRUNCATE)) {
switch (sysio()->error.ftruncate) {
case Noux::Sysio::FTRUNCATE_ERR_NO_PERM: errno = EPERM; break;
}
return -1;
}
return 0;
}
int Plugin::fcntl(Libc::File_descriptor *fd, int cmd, long arg)
{
/* copy arguments to sysio */

View File

@ -518,6 +518,7 @@ namespace Noux {
bool write(Sysio *sysio, Vfs_handle *handle) { return false; }
bool read(Sysio *sysio, Vfs_handle *vfs_handle) { return false; }
bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle) { return false; }
};
}

View File

@ -24,8 +24,9 @@ namespace Noux {
*/
struct File_io_service
{
virtual bool write(Sysio *sysio, Vfs_handle *vfs_handle) = 0;
virtual bool read(Sysio *sysio, Vfs_handle *vfs_handle) = 0;
virtual bool write(Sysio *sysio, Vfs_handle *vfs_handle) = 0;
virtual bool read(Sysio *sysio, Vfs_handle *vfs_handle) = 0;
virtual bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle) = 0;
};
}

View File

@ -516,6 +516,27 @@ namespace Noux {
source.release_packet(packet);
return true;
}
bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle)
{
Fs_vfs_handle const *handle = static_cast<Fs_vfs_handle *>(vfs_handle);
Sysio::Ftruncate_error error;
try {
_fs.truncate(handle->file_handle(), sysio->ftruncate_in.length);
return true;
} catch (::File_system::Invalid_handle) {
/* should not happen */
error = Sysio::FTRUNCATE_ERR_NO_PERM;
} catch (::File_system::Permission_denied) {
error = Sysio::FTRUNCATE_ERR_NO_PERM;
}
sysio->error.ftruncate = error;
return false;
}
};
}

View File

@ -50,14 +50,15 @@ namespace Noux {
virtual ~Io_channel() { }
virtual bool write(Sysio *sysio, size_t &count) { return false; }
virtual bool read(Sysio *sysio) { return false; }
virtual bool fstat(Sysio *sysio) { return false; }
virtual bool fcntl(Sysio *sysio) { return false; }
virtual bool fchdir(Sysio *sysio, Pwd *pwd) { return false; }
virtual bool dirent(Sysio *sysio) { return false; }
virtual bool ioctl(Sysio *sysio) { return false; }
virtual bool lseek(Sysio *sysio) { return false; }
virtual bool write(Sysio *sysio, size_t &count) { return false; }
virtual bool read(Sysio *sysio) { return false; }
virtual bool fstat(Sysio *sysio) { return false; }
virtual bool ftruncate(Sysio *sysio) { return false; }
virtual bool fcntl(Sysio *sysio) { return false; }
virtual bool fchdir(Sysio *sysio, Pwd *pwd) { return false; }
virtual bool dirent(Sysio *sysio) { return false; }
virtual bool ioctl(Sysio *sysio) { return false; }
virtual bool lseek(Sysio *sysio) { return false; }
/**
* Return true if an unblocking condition of the channel is satisfied

View File

@ -121,6 +121,16 @@ bool Noux::Child::syscall(Noux::Session::Syscall sc)
return true;
}
case SYSCALL_FTRUNCATE:
{
Shared_pointer<Io_channel> io = _lookup_channel(_sysio->ftruncate_in.fd);
while (!io->check_unblock(true, false, false))
_block_for_io_channel(io);
return io->ftruncate(_sysio);
}
case SYSCALL_STAT:
case SYSCALL_LSTAT: /* XXX implement difference between 'lstat' and 'stat' */

View File

@ -468,6 +468,12 @@ namespace Noux {
sysio->read_out.count = count;
return true;
}
bool ftruncate(Sysio *sysio, Vfs_handle *handle)
{
PDBG("called\n");
return false;
}
};
}

View File

@ -204,6 +204,8 @@ namespace Noux {
sysio->read_out.count = _terminal.read(sysio->read_out.chunk, sysio->read_in.count);
return true;
}
bool ftruncate(Sysio *sysio, Vfs_handle *vfs_handle) { return false; }
};
}

View File

@ -74,8 +74,9 @@ namespace Noux {
static bool _msg(char const *sc) {
PERR("%s not supported by file system", sc); return false; }
bool write(Sysio *sysio, Vfs_handle *handle) { return _msg("write"); }
bool read(Sysio *sysio, Vfs_handle *handle) { return _msg("read"); }
bool write(Sysio *sysio, Vfs_handle *handle) { return _msg("write"); }
bool read(Sysio *sysio, Vfs_handle *handle) { return _msg("read"); }
bool ftruncate(Sysio *sysio, Vfs_handle *handle) { return _msg("ftruncate"); }
};
static Pseudo_file_io_service fs;
return &fs;

View File

@ -65,6 +65,11 @@ namespace Noux {
return result;
}
bool ftruncate(Sysio *sysio)
{
return _fh->fs()->ftruncate(sysio, _fh);
}
bool fcntl(Sysio *sysio)
{
switch (sysio->fcntl_in.cmd) {