// Copyright (c) 2008 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.h" #include #include #include #include #include #include "base/basictypes.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/string_util.h" #include "net/base/net_errors.h" // 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(net::FROM_BEGIN == SEEK_SET && net::FROM_CURRENT == SEEK_CUR && net::FROM_END == SEEK_END, whence_matches_system); namespace net { // FileStream::AsyncContext ---------------------------------------------- // TODO(deanm): Figure out how to best do async IO. class FileStream::AsyncContext { public: CompletionCallback* callback() const { return NULL; } private: DISALLOW_COPY_AND_ASSIGN(AsyncContext); }; // FileStream ------------------------------------------------------------ FileStream::FileStream() : file_(base::kInvalidPlatformFileValue) { DCHECK(!IsOpen()); } FileStream::~FileStream() { Close(); } void FileStream::Close() { if (file_ != base::kInvalidPlatformFileValue) { if (close(file_) != 0) { NOTREACHED(); } file_ = base::kInvalidPlatformFileValue; } async_context_.reset(); } // Map from errno to net error codes. static int64 MapErrorCode(int err) { switch(err) { case ENOENT: return ERR_FILE_NOT_FOUND; case EACCES: return ERR_ACCESS_DENIED; default: LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED"; return ERR_FAILED; } } int FileStream::Open(const std::wstring& path, int open_flags) { if (IsOpen()) { DLOG(FATAL) << "File is already open!"; return ERR_UNEXPECTED; } open_flags_ = open_flags; file_ = base::CreatePlatformFile(path, open_flags_, NULL); if (file_ == base::kInvalidPlatformFileValue) { LOG(WARNING) << "Failed to open file: " << errno; return MapErrorCode(errno); } return OK; } bool FileStream::IsOpen() const { return file_ != base::kInvalidPlatformFileValue; } int64 FileStream::Seek(Whence whence, int64 offset) { if (!IsOpen()) return ERR_UNEXPECTED; // If we're in async, make sure we don't have a request in flight. DCHECK(!async_context_.get() || !async_context_->callback()); off_t res = lseek(file_, static_cast(offset), static_cast(whence)); if (res == static_cast(-1)) return MapErrorCode(errno); return res; } int64 FileStream::Available() { if (!IsOpen()) return ERR_UNEXPECTED; int64 cur_pos = Seek(FROM_CURRENT, 0); if (cur_pos < 0) return cur_pos; struct stat info; if (fstat(file_, &info) != 0) return MapErrorCode(errno); int64 size = static_cast(info.st_size); DCHECK(size >= cur_pos); return size - cur_pos; } // TODO(deanm): async. int FileStream::Read( char* buf, int buf_len, CompletionCallback* callback) { // read(..., 0) will return 0, which indicates end-of-file. DCHECK(buf_len > 0 && buf_len <= SSIZE_MAX); if (!IsOpen()) return ERR_UNEXPECTED; // Loop in the case of getting interrupted by a signal. for (;;) { ssize_t res = read(file_, buf, static_cast(buf_len)); if (res == static_cast(-1)) { if (errno == EINTR) continue; return MapErrorCode(errno); } return static_cast(res); } } // TODO(deanm): async. int FileStream::Write( const char* buf, int buf_len, CompletionCallback* callback) { // read(..., 0) will return 0, which indicates end-of-file. DCHECK(buf_len > 0 && buf_len <= SSIZE_MAX); if (!IsOpen()) return ERR_UNEXPECTED; int total_bytes_written = 0; size_t len = static_cast(buf_len); while (total_bytes_written < buf_len) { ssize_t res = write(file_, buf, len); if (res == static_cast(-1)) { if (errno == EINTR) continue; return MapErrorCode(errno); } total_bytes_written += res; buf += res; len -= res; } return total_bytes_written; } } // namespace net