| /* | |
| * Copyright (C) 2014 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| // We need 32-bit functions here. Suppress unconditional use of 64-bit offsets. | |
| // Functions with 64-bit offsets are still available when used with "64" suffix. | |
| // | |
| // Note: this is actually only needed for host build since Android build system | |
| // insists on defining _FILE_OFFSET_BITS=64 for host-host binaries. | |
| // | |
| // _FILE_OFFSET_BITS is NOT defined when we are building target-host binaries. | |
| #ifdef _FILE_OFFSET_BITS | |
| #undef _FILE_OFFSET_BITS | |
| #endif | |
| #include "berberis/kernel_api/fcntl_emulation.h" | |
| #include <fcntl.h> | |
| #include <sys/file.h> | |
| #include <cerrno> | |
| #include "berberis/base/checks.h" | |
| #include "berberis/kernel_api/open_emulation.h" | |
| #include "berberis/kernel_api/tracing.h" | |
| static_assert(F_DUPFD == 0); | |
| static_assert(F_GETFD == 1); | |
| static_assert(F_SETFD == 2); | |
| static_assert(F_GETFL == 3); | |
| static_assert(F_SETFL == 4); | |
| static_assert(F_SETOWN == 8); | |
| static_assert(F_GETOWN == 9); | |
| static_assert(F_SETSIG == 10); | |
| static_assert(F_GETSIG == 11); | |
| static_assert(F_SETOWN_EX == 15); | |
| static_assert(F_GETOWN_EX == 16); | |
| static_assert(F_OWNER_TID == 0); | |
| static_assert(F_OWNER_PID == 1); | |
| static_assert(F_OWNER_PGRP == 2); | |
| static_assert(F_RDLCK == 0); | |
| static_assert(F_WRLCK == 1); | |
| static_assert(F_UNLCK == 2); | |
| #ifdef F_EXLCK | |
| static_assert(F_EXLCK == 4); | |
| #endif | |
| #ifdef F_SHLCK | |
| static_assert(F_SHLCK == 8); | |
| #endif | |
| static_assert(F_SETLEASE == 1024); | |
| static_assert(F_GETLEASE == 1025); | |
| static_assert(F_NOTIFY == 1026); | |
| #if !defined(ANDROID_HOST_MUSL) | |
| static_assert(F_GETLK == 5); | |
| static_assert(F_SETLK == 6); | |
| static_assert(F_SETLKW == 7); | |
| #endif | |
| #define GUEST_F_GETLK 5 | |
| #define GUEST_F_SETLK 6 | |
| #define GUEST_F_SETLKW 7 | |
| #if defined(ANDROID_HOST_MUSL) | |
| // Musl only has a 64-bit flock that it uses for flock and flock64. | |
| struct Guest_flock { | |
| int16_t l_type; | |
| int16_t l_whence; | |
| int32_t l_start; | |
| int32_t l_len; | |
| int32_t l_pid; | |
| }; | |
| const struct flock64* ConvertGuestFlockToHostFlock64(const Guest_flock* guest, | |
| struct flock64* host) { | |
| if (!guest) { | |
| return nullptr; | |
| } | |
| *host = {guest->l_type, guest->l_whence, guest->l_start, guest->l_len, guest->l_pid}; | |
| return host; | |
| } | |
| void ConvertHostFlock64ToGuestFlock(const struct flock64* host, Guest_flock* guest) { | |
| CHECK_NE(guest, nullptr); | |
| CHECK_LE(host->l_start, INT32_MAX); | |
| CHECK_GE(host->l_start, INT32_MIN); | |
| CHECK_LE(host->l_len, INT32_MAX); | |
| CHECK_GE(host->l_len, INT32_MIN); | |
| *guest = {host->l_type, | |
| host->l_whence, | |
| static_cast<int32_t>(host->l_start), | |
| static_cast<int32_t>(host->l_len), | |
| host->l_pid}; | |
| } | |
| #endif | |
| namespace berberis { | |
| int GuestFcntl(int fd, int cmd, long arg_3) { | |
| auto [processed, result] = GuestFcntlArch(fd, cmd, arg_3); | |
| if (processed) { | |
| return result; | |
| } | |
| switch (cmd) { | |
| case F_GETFD: | |
| case F_GETOWN: | |
| case F_GETSIG: | |
| case F_GETLEASE: | |
| return fcntl(fd, cmd); | |
| case F_GETFL: { | |
| auto result = fcntl(fd, cmd); | |
| if (result < 0) { | |
| return result; | |
| } | |
| return ToGuestOpenFlags(result); | |
| } | |
| case F_DUPFD: | |
| case F_DUPFD_CLOEXEC: | |
| case F_SETFD: | |
| case F_SETOWN: | |
| case F_SETSIG: | |
| case F_SETLEASE: | |
| case F_NOTIFY: | |
| case F_GETOWN_EX: | |
| case F_SETOWN_EX: | |
| #if defined(F_ADD_SEALS) | |
| case F_ADD_SEALS: | |
| #endif | |
| #if defined(F_GET_SEALS) | |
| case F_GET_SEALS: | |
| #endif | |
| case GUEST_F_SETLK: | |
| case GUEST_F_SETLKW: | |
| case GUEST_F_GETLK: | |
| #if defined(ANDROID_HOST_MUSL) | |
| { | |
| // Musl only has a 64-bit flock for both flock and flock64, translate flock calls to flock64. | |
| Guest_flock* guest_flock = reinterpret_cast<Guest_flock*>(arg_3); | |
| struct flock64 host_flock64; | |
| // In case of GETLK input flock describes region | |
| // to check, thus conversion is also required. | |
| auto result = fcntl(fd, | |
| cmd + F_SETLK - GUEST_F_SETLK, | |
| ConvertGuestFlockToHostFlock64(guest_flock, &host_flock64)); | |
| if (result == 0 && cmd == GUEST_F_GETLK) { | |
| // Output contains the result of lock check. | |
| ConvertHostFlock64ToGuestFlock(&host_flock64, guest_flock); | |
| } | |
| return result; | |
| } | |
| #else | |
| // struct flock compatibility is checked above. | |
| return fcntl(fd, cmd, arg_3); | |
| #endif | |
| case F_SETFL: | |
| return fcntl(fd, cmd, ToHostOpenFlags(arg_3)); | |
| default: | |
| KAPI_TRACE("Unknown fcntl command: %d", cmd); | |
| errno = ENOSYS; | |
| return -1; | |
| } | |
| } | |
| } // namespace berberis |