blob: 2e399a24d083a5576e9aaa166ec33bbfa381f77a [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// For 64-bit file access (off_t = off64_t, lseek64, etc).
#define _FILE_OFFSET_BITS 64
#include "net/base/file_stream_context.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/file_path.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/posix/eintr_wrapper.h"
#include "base/task_runner_util.h"
#include "base/threading/worker_pool.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#if defined(OS_ANDROID) || defined(__LB_ANDROID__)
// Android's bionic libc only supports the LFS transitional API.
#define off_t off64_t
#define lseek lseek64
#define stat stat64
#define fstat fstat64
#endif
#if defined(__LB_PS3__)
// PS3 off_t and lseek are 32 bits, but stats are all 64.
#define off_t off64_t
#define lseek lseek64
#endif
#if defined(COBALT_WIN)
#define off_t __int64
#define lseek _lseeki64
#endif
namespace net {
// We cast back and forth, so make sure it's the size we're expecting.
COMPILE_ASSERT(sizeof(int64) == sizeof(off_t), off_t_64_bit);
// Make sure our Whence mappings match the system headers.
COMPILE_ASSERT(FROM_BEGIN == SEEK_SET &&
FROM_CURRENT == SEEK_CUR &&
FROM_END == SEEK_END, whence_matches_system);
FileStream::Context::Context(const BoundNetLog& bound_net_log)
: file_(base::kInvalidPlatformFileValue),
record_uma_(false),
async_in_progress_(false),
orphaned_(false),
bound_net_log_(bound_net_log) {
}
FileStream::Context::Context(base::PlatformFile file,
const BoundNetLog& bound_net_log,
int /* open_flags */)
: file_(file),
record_uma_(false),
async_in_progress_(false),
orphaned_(false),
bound_net_log_(bound_net_log) {
}
FileStream::Context::~Context() {
}
int64 FileStream::Context::GetFileSize() const {
struct stat info;
if (fstat(file_, &info) != 0)
return RecordAndMapError(errno, FILE_ERROR_SOURCE_GET_SIZE);
return static_cast<int64>(info.st_size);
}
int FileStream::Context::ReadAsync(IOBuffer* in_buf,
int buf_len,
const CompletionCallback& callback) {
DCHECK(!async_in_progress_);
scoped_refptr<IOBuffer> buf = in_buf;
const bool posted = base::PostTaskAndReplyWithResult(
base::WorkerPool::GetTaskRunner(true /* task is slow */),
FROM_HERE,
base::Bind(&Context::ReadFileImpl,
base::Unretained(this), buf, buf_len),
base::Bind(&Context::ProcessAsyncResult,
base::Unretained(this), IntToInt64(callback),
FILE_ERROR_SOURCE_READ));
DCHECK(posted);
async_in_progress_ = true;
return ERR_IO_PENDING;
}
int FileStream::Context::ReadSync(char* in_buf, int buf_len) {
scoped_refptr<IOBuffer> buf = new WrappedIOBuffer(in_buf);
int64 result = ReadFileImpl(buf, buf_len);
CheckForIOError(&result, FILE_ERROR_SOURCE_READ);
return result;
}
int FileStream::Context::WriteAsync(IOBuffer* in_buf,
int buf_len,
const CompletionCallback& callback) {
DCHECK(!async_in_progress_);
scoped_refptr<IOBuffer> buf = in_buf;
const bool posted = base::PostTaskAndReplyWithResult(
base::WorkerPool::GetTaskRunner(true /* task is slow */),
FROM_HERE,
base::Bind(&Context::WriteFileImpl,
base::Unretained(this), buf, buf_len),
base::Bind(&Context::ProcessAsyncResult,
base::Unretained(this), IntToInt64(callback),
FILE_ERROR_SOURCE_WRITE));
DCHECK(posted);
async_in_progress_ = true;
return ERR_IO_PENDING;
}
int FileStream::Context::WriteSync(const char* in_buf, int buf_len) {
scoped_refptr<IOBuffer> buf = new WrappedIOBuffer(in_buf);
int64 result = WriteFileImpl(buf, buf_len);
CheckForIOError(&result, FILE_ERROR_SOURCE_WRITE);
return result;
}
int FileStream::Context::Truncate(int64 bytes) {
int result = ftruncate(file_, bytes);
if (result == 0)
return bytes;
return RecordAndMapError(errno, FILE_ERROR_SOURCE_SET_EOF);
}
int64 FileStream::Context::SeekFileImpl(Whence whence, int64 offset) {
off_t res = lseek(file_, static_cast<off_t>(offset),
static_cast<int>(whence));
if (res == static_cast<off_t>(-1))
return errno;
return res;
}
int64 FileStream::Context::FlushFileImpl() {
ssize_t res = HANDLE_EINTR(fsync(file_));
if (res == -1)
return errno;
return res;
}
int64 FileStream::Context::ReadFileImpl(scoped_refptr<IOBuffer> buf,
int buf_len) {
// Loop in the case of getting interrupted by a signal.
ssize_t res = HANDLE_EINTR(read(file_, buf->data(),
static_cast<size_t>(buf_len)));
if (res == -1)
return errno;
return res;
}
int64 FileStream::Context::WriteFileImpl(scoped_refptr<IOBuffer> buf,
int buf_len) {
ssize_t res = HANDLE_EINTR(write(file_, buf->data(), buf_len));
if (res == -1)
return errno;
return res;
}
} // namespace net