blob: 748694357185c9987fde3e87c8b9032bbca49140 [file] [log] [blame]
/*
* 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.
*/
#include "open_emulation.h"
// Documentation says that to get access to the constants used below one
// must include these three files. In reality it looks as if all constants
// are defined by <fcntl.h>, but we include all three as described in docs.
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "berberis/base/logging.h"
#include "guest_types.h"
#include "main_executable_real_path_emulation.h"
namespace berberis {
#if !defined(__i386__) && !defined(__x86_64__)
#error Currently open flags conversion is only supported on x86
#endif
// Glibc doesn't expose __O_SYNC
#if !defined(__O_SYNC)
#if defined(__BIONIC__)
#error __O_SYNC undefined in bionic
#endif
#define __O_SYNC 04000000
#endif
// Musl defines an O_SEARCH flag an includes it in O_ACCMODE,
// bionic and glibc don't.
#ifndef O_SEARCH
#define O_SEARCH 0
#endif
static_assert((O_ACCMODE & ~O_SEARCH) == 00000003);
// These flags should have the same value on all architectures.
static_assert(O_CREAT == 00000100);
static_assert(O_EXCL == 00000200);
static_assert(O_NOCTTY == 00000400);
static_assert(O_TRUNC == 00001000);
static_assert(O_APPEND == 00002000);
static_assert(O_NONBLOCK == 00004000);
static_assert(O_DSYNC == 00010000);
static_assert(FASYNC == 00020000);
static_assert(O_NOATIME == 01000000);
static_assert(O_CLOEXEC == 02000000);
static_assert(__O_SYNC == 04000000);
static_assert(O_SYNC == (O_DSYNC | __O_SYNC));
static_assert(O_PATH == 010000000);
namespace {
const int kCompatibleOpenFlags = O_ACCMODE | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | O_APPEND |
O_NONBLOCK | O_DSYNC | FASYNC | O_NOATIME | O_CLOEXEC | __O_SYNC |
O_PATH;
} // namespace
int ToHostOpenFlags(int guest_flags) {
const int kIncompatibleGuestOpenFlags =
GUEST_O_DIRECTORY | GUEST_O_NOFOLLOW | GUEST_O_DIRECT | GUEST_O_LARGEFILE;
int unknown_guest_flags = guest_flags & ~(kCompatibleOpenFlags | kIncompatibleGuestOpenFlags);
if (unknown_guest_flags) {
ALOGE("Unsupported guest open flags: original=0x%x unsupported=0x%x",
guest_flags,
unknown_guest_flags);
}
int host_flags = guest_flags & ~kIncompatibleGuestOpenFlags;
if (guest_flags & GUEST_O_DIRECTORY) {
host_flags |= O_DIRECTORY;
}
if (guest_flags & GUEST_O_NOFOLLOW) {
host_flags |= O_NOFOLLOW;
}
if (guest_flags & GUEST_O_DIRECT) {
host_flags |= O_DIRECT;
}
if (guest_flags & GUEST_O_LARGEFILE) {
host_flags |= O_LARGEFILE;
}
return host_flags;
}
int ToGuestOpenFlags(int host_flags) {
const int kIncompatibleHostOpenFlags = O_DIRECTORY | O_NOFOLLOW | O_DIRECT | O_LARGEFILE;
int unknown_host_flags = host_flags & ~(kCompatibleOpenFlags | kIncompatibleHostOpenFlags);
if (unknown_host_flags) {
ALOGE("Unsupported host open flags: original=0x%x unsupported=0x%x",
host_flags,
unknown_host_flags);
}
int guest_flags = host_flags & ~kIncompatibleHostOpenFlags;
if (host_flags & O_DIRECTORY) {
guest_flags |= GUEST_O_DIRECTORY;
}
if (host_flags & O_NOFOLLOW) {
guest_flags |= GUEST_O_NOFOLLOW;
}
if (host_flags & O_DIRECT) {
guest_flags |= GUEST_O_DIRECT;
}
if (host_flags & O_LARGEFILE) {
guest_flags |= GUEST_O_LARGEFILE;
}
return guest_flags;
}
int OpenatForGuest(int dirfd, const char* path, int guest_flags, mode_t mode) {
int host_flags = ToHostOpenFlags(guest_flags);
const char* real_path = nullptr;
if ((host_flags & AT_SYMLINK_NOFOLLOW) == 0) {
real_path = TryReadLinkToMainExecutableRealPath(path);
}
return openat(dirfd, real_path ? real_path : path, host_flags, mode);
}
int OpenForGuest(const char* path, int flags, mode_t mode) {
return OpenatForGuest(AT_FDCWD, path, flags, mode);
}
} // namespace berberis