// Copyright 2015 The Bazel Authors. All rights reserved.
//
// 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 <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <algorithm>
#include <limits>
#include "third_party/ijar/mapped_file.h"
#define MAX_ERROR 2048
namespace devtools_ijar {
static char errmsg[MAX_ERROR];
struct MappedInputFileImpl {
size_t discarded_;
int fd_;
};
MappedInputFile::MappedInputFile(const char* name) {
impl_ = NULL;
opened_ = false;
int fd = open(name, O_RDONLY);
if (fd < 0) {
snprintf(errmsg, MAX_ERROR, "open(): %s", strerror(errno));
errmsg_ = errmsg;
return;
}
off_t length = lseek(fd, 0, SEEK_END);
if (length < 0) {
snprintf(errmsg, MAX_ERROR, "lseek(): %s", strerror(errno));
errmsg_ = errmsg;
return;
}
void* buffer = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
if (buffer == MAP_FAILED) {
snprintf(errmsg, MAX_ERROR, "mmap(): %s", strerror(errno));
errmsg_ = errmsg;
return;
}
impl_ = new MappedInputFileImpl();
impl_->fd_ = fd;
impl_->discarded_ = 0;
buffer_ = reinterpret_cast<u1*>(buffer);
length_ = length;
opened_ = true;
}
MappedInputFile::~MappedInputFile() {
delete impl_;
}
void MappedInputFile::Discard(size_t bytes) {
munmap(buffer_ + impl_->discarded_, bytes);
impl_->discarded_ += bytes;
}
int MappedInputFile::Close() {
if (close(impl_->fd_) < 0) {
snprintf(errmsg, MAX_ERROR, "close(): %s", strerror(errno));
errmsg_ = errmsg;
return -1;
}
return 0;
}
struct MappedOutputFileImpl {
int fd_;
int mmap_length_;
};
MappedOutputFile::MappedOutputFile(const char* name, size_t estimated_size)
: estimated_size_(estimated_size) {
impl_ = NULL;
opened_ = false;
int fd = open(name, O_CREAT|O_RDWR|O_TRUNC, 0644);
if (fd < 0) {
snprintf(errmsg, MAX_ERROR, "open(): %s", strerror(errno));
errmsg_ = errmsg;
return;
}
// Create mmap-able sparse file
if (ftruncate(fd, estimated_size) < 0) {
snprintf(errmsg, MAX_ERROR, "ftruncate(): %s", strerror(errno));
errmsg_ = errmsg;
return;
}
// Ensure that any buffer overflow in JarStripper will result in
// SIGSEGV or SIGBUS by over-allocating beyond the end of the file.
size_t mmap_length =
std::min(static_cast<size_t>(estimated_size + sysconf(_SC_PAGESIZE)),
std::numeric_limits<size_t>::max());
void* mapped =
mmap(NULL, mmap_length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mapped == MAP_FAILED) {
snprintf(errmsg, MAX_ERROR, "mmap(): %s", strerror(errno));
errmsg_ = errmsg;
return;
}
impl_ = new MappedOutputFileImpl();
impl_->fd_ = fd;
impl_->mmap_length_ = mmap_length;
buffer_ = reinterpret_cast<u1*>(mapped);
opened_ = true;
}
MappedOutputFile::~MappedOutputFile() {
delete impl_;
}
int MappedOutputFile::Close(size_t size) {
if (size > estimated_size_) {
snprintf(errmsg, MAX_ERROR, "size %zu > estimated size %zu", size,
estimated_size_);
errmsg_ = errmsg;
return -1;
}
munmap(buffer_, impl_->mmap_length_);
if (ftruncate(impl_->fd_, size) < 0) {
snprintf(errmsg, MAX_ERROR, "ftruncate(): %s", strerror(errno));
errmsg_ = errmsg;
return -1;
}
if (close(impl_->fd_) < 0) {
snprintf(errmsg, MAX_ERROR, "close(): %s", strerror(errno));
errmsg_ = errmsg;
return -1;
}
return 0;
}
} // namespace devtools_ijar