From 280fc59edfb7b556936922c758841ba1fd84d7b6 Mon Sep 17 00:00:00 2001 From: Christian Prochaska Date: Mon, 6 Aug 2012 18:52:37 +0200 Subject: [PATCH] Fix 'O_CREAT' flag handling in 'open()' Fixes #313. --- libports/src/lib/libc_ffat/plugin.cc | 2 +- libports/src/lib/libc_fs/plugin.cc | 24 +++++++++++++-- libports/src/server/ffat_fs/main.cc | 2 +- libports/src/test/libc_ffat/main.cc | 4 +++ ports/include/noux_session/sysio.h | 5 ++-- ports/src/lib/libc_noux/plugin.cc | 44 ++++++++++++++++++++-------- ports/src/noux/fs_file_system.h | 5 +++- 7 files changed, 65 insertions(+), 21 deletions(-) diff --git a/libports/src/lib/libc_ffat/plugin.cc b/libports/src/lib/libc_ffat/plugin.cc index ab88d5abc..52059a1f8 100644 --- a/libports/src/lib/libc_ffat/plugin.cc +++ b/libports/src/lib/libc_ffat/plugin.cc @@ -469,7 +469,7 @@ class Plugin : public Libc::Plugin if ((flags & O_EXCL) == O_EXCL) ffat_flags |= FA_CREATE_NEW; else - ffat_flags |= FA_CREATE_ALWAYS; + ffat_flags |= FA_OPEN_ALWAYS; } FRESULT res = f_open(&ffat_file, pathname, ffat_flags); diff --git a/libports/src/lib/libc_fs/plugin.cc b/libports/src/lib/libc_fs/plugin.cc index 2501bdf0d..6e2713c79 100644 --- a/libports/src/lib/libc_fs/plugin.cc +++ b/libports/src/lib/libc_fs/plugin.cc @@ -499,16 +499,34 @@ class Plugin : public Libc::Plugin * Open directory that contains the file to be opened/created */ File_system::Dir_handle const dir_handle = - file_system()->dir(dir_path, false); + file_system()->dir(dir_path, false); Node_handle_guard guard(dir_handle); + File_system::File_handle handle; + /* * Open or create file */ bool const create = (flags & O_CREAT) != 0; - File_system::File_handle const handle = - file_system()->file(dir_handle, basename, mode, create); + + bool opened = false; + while (!opened) { + try { + handle = file_system()->file(dir_handle, basename, mode, create); + opened = true; + } catch (File_system::Node_already_exists) { + if (flags & O_EXCL) + throw File_system::Node_already_exists(); + /* try to open the existing file */ + try { + handle = file_system()->file(dir_handle, basename, mode, false); + opened = true; + } catch (File_system::Lookup_failed) { + /* the file got deleted in the meantime */ + } + } + } Plugin_context *context = new (Genode::env()->heap()) Plugin_context(handle); diff --git a/libports/src/server/ffat_fs/main.cc b/libports/src/server/ffat_fs/main.cc index b380a71ab..e31ebc4fa 100644 --- a/libports/src/server/ffat_fs/main.cc +++ b/libports/src/server/ffat_fs/main.cc @@ -277,7 +277,7 @@ namespace File_system { throw Permission_denied(); if (create) - ffat_flags |= FA_CREATE_ALWAYS; /* overwrite existing file */ + ffat_flags |= FA_CREATE_NEW; if ((mode == READ_ONLY) || (mode == READ_WRITE)) ffat_flags |= FA_READ; diff --git a/libports/src/test/libc_ffat/main.cc b/libports/src/test/libc_ffat/main.cc index 9f7d43360..e849b29e6 100644 --- a/libports/src/test/libc_ffat/main.cc +++ b/libports/src/test/libc_ffat/main.cc @@ -73,6 +73,10 @@ int main(int argc, char *argv[]) CALL_AND_CHECK(count, write(fd, pattern, pattern_size), (size_t)count == pattern_size, ""); CALL_AND_CHECK(ret, close(fd), ret == 0, ""); + /* open the file with O_CREAT again (should have no effect on the file) */ + CALL_AND_CHECK(fd, open(file_name, O_CREAT | O_WRONLY), fd >= 0, "file_name=%s", file_name); + CALL_AND_CHECK(ret, close(fd), ret == 0, ""); + /* query file status of new file */ struct stat stat_buf; CALL_AND_CHECK(ret, stat(file_name, &stat_buf), ret == 0, "file_name=%s", file_name); diff --git a/ports/include/noux_session/sysio.h b/ports/include/noux_session/sysio.h index af1732f71..97406a6d1 100644 --- a/ports/include/noux_session/sysio.h +++ b/ports/include/noux_session/sysio.h @@ -67,7 +67,7 @@ namespace Noux { OPEN_MODE_WRONLY = 1, OPEN_MODE_RDWR = 2, OPEN_MODE_ACCMODE = 3, - OPEN_MODE_CREATE = 0x0200, + OPEN_MODE_CREATE = 0x0800, /* libc O_EXCL */ }; enum { @@ -275,7 +275,8 @@ namespace Noux { 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 }; + 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, diff --git a/ports/src/lib/libc_noux/plugin.cc b/ports/src/lib/libc_noux/plugin.cc index 6fead25ab..7bae6304e 100644 --- a/ports/src/lib/libc_noux/plugin.cc +++ b/ports/src/lib/libc_noux/plugin.cc @@ -607,19 +607,37 @@ namespace { return 0; } - if (flags & O_CREAT) - unlink(pathname); - - Genode::strncpy(sysio()->open_in.path, pathname, sizeof(sysio()->open_in.path)); - sysio()->open_in.mode = flags; - - if (!noux()->syscall(Noux::Session::SYSCALL_OPEN)) { - /* - * XXX we should return meaningful errno values - */ - PDBG("ENOENT (sysio()->error.open=%d)", sysio()->error.open); - errno = ENOENT; - return 0; + bool opened = false; + while (!opened) { + Genode::strncpy(sysio()->open_in.path, pathname, sizeof(sysio()->open_in.path)); + sysio()->open_in.mode = flags; + if (noux()->syscall(Noux::Session::SYSCALL_OPEN)) + opened = true; + else + switch (sysio()->error.open) { + case Noux::Sysio::OPEN_ERR_UNACCESSIBLE: + if (!(flags & O_CREAT)) { + errno = ENOENT; + return 0; + } + /* O_CREAT is set, so try to create the file */ + Genode::strncpy(sysio()->open_in.path, pathname, sizeof(sysio()->open_in.path)); + sysio()->open_in.mode = flags | O_EXCL; + if (noux()->syscall(Noux::Session::SYSCALL_OPEN)) + opened = true; + else + switch (sysio()->error.open) { + case Noux::Sysio::OPEN_ERR_EXISTS: + /* file has been created by someone else in the meantime */ + break; + case Noux::Sysio::OPEN_ERR_NO_PERM: errno = EPERM; return 0; + default: errno = ENOENT; return 0; + } + break; + case Noux::Sysio::OPEN_ERR_NO_PERM: errno = EPERM; return 0; + case Noux::Sysio::OPEN_ERR_EXISTS: errno = EEXIST; return 0; + default: errno = ENOENT; return 0; + } } Libc::Plugin_context *context = noux_context(sysio()->open_out.fd); diff --git a/ports/src/noux/fs_file_system.h b/ports/src/noux/fs_file_system.h index 18c25ecb5..8277a8a89 100644 --- a/ports/src/noux/fs_file_system.h +++ b/ports/src/noux/fs_file_system.h @@ -416,7 +416,10 @@ namespace Noux { error = Sysio::OPEN_ERR_NO_PERM; } catch (::File_system::Invalid_handle) { error = Sysio::OPEN_ERR_NO_PERM; } - catch (::File_system::Lookup_failed) { } + catch (::File_system::Lookup_failed) { + error = Sysio::OPEN_ERR_UNACCESSIBLE; } + catch (::File_system::Node_already_exists) { + error = Sysio::OPEN_ERR_EXISTS; } sysio->error.open = error; return 0;