1/* 2 * Copyright 2012, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "bcc/Support/OutputFile.h" 18 19#include <cstdlib> 20 21#include <llvm/Support/raw_ostream.h> 22 23#include "bcc/Support/Log.h" 24 25using namespace bcc; 26 27OutputFile *OutputFile::CreateTemporary(const std::string &pFileTemplate, 28 unsigned pFlags) { 29 char *tmp_filename = NULL; 30 int tmp_fd; 31 OutputFile *result = NULL; 32 33 // Allocate memory to hold the generated unique temporary filename. 34 tmp_filename = 35 new (std::nothrow) char [ pFileTemplate.length() + /* .XXXXXX */7 + 1 ]; 36 if (tmp_filename == NULL) { 37 ALOGE("Out of memory when allocates memory for filename %s in " 38 "OutputFile::CreateTemporary()!", pFileTemplate.c_str()); 39 return NULL; 40 } 41 42 // Construct filename template for mkstemp(). 43 if (pFileTemplate.length() > 0) 44 ::memcpy(tmp_filename, pFileTemplate.c_str(), pFileTemplate.length()); 45 ::strncpy(tmp_filename + pFileTemplate.length(), ".XXXXXX", 7); 46 47 // POSIX mkstemp() never returns EINTR. 48 tmp_fd = ::mkstemp(tmp_filename); 49 if (tmp_fd < 0) { 50 llvm::error_code err(errno, llvm::posix_category()); 51 ALOGE("Failed to create temporary file using mkstemp() for %s! (%s)", 52 tmp_filename, err.message().c_str()); 53 delete [] tmp_filename; 54 return NULL; 55 } 56 57 // Create result OutputFile. Temporary file is always truncated. 58 result = new (std::nothrow) OutputFile(tmp_filename, 59 pFlags | FileBase::kTruncate); 60 if (result == NULL) { 61 ALOGE("Out of memory when creates OutputFile for %s!", tmp_filename); 62 // Fall through to the clean-up codes. 63 } else { 64 if (result->hasError()) { 65 ALOGE("Failed to open temporary output file %s! (%s)", 66 result->getName().c_str(), result->getErrorMessage().c_str()); 67 delete result; 68 result = NULL; 69 // Fall through to the clean-up codes. 70 } 71 } 72 73 // Clean up. 74 delete [] tmp_filename; 75 ::close(tmp_fd); 76 77 return result; 78} 79 80OutputFile::OutputFile(const std::string &pFilename, unsigned pFlags) 81 : super(pFilename, pFlags) { } 82 83ssize_t OutputFile::write(const void *pBuf, size_t count) { 84 if ((mFD < 0) || hasError()) { 85 return -1; 86 } 87 88 if ((count <= 0) || (pBuf == NULL)) { 89 // Keep safe and issue a warning. 90 ALOGW("OutputFile::write: count = %zu, buffer = %p", count, pBuf); 91 return 0; 92 } 93 94 while (count > 0) { 95 ssize_t write_size = ::write(mFD, pBuf, count); 96 97 if (write_size > 0) { 98 return write_size; 99 } else if ((errno == EAGAIN) || (errno == EINTR)) { 100 // If the errno is EAGAIN or EINTR, then we try to write again. 101 // 102 // Fall-through 103 } else { 104 detectError(); 105 return -1; 106 } 107 } 108 // unreachable 109 return 0; 110} 111 112void OutputFile::truncate() { 113 if (mFD < 0) { 114 return; 115 } 116 117 do { 118 if (::ftruncate(mFD, 0) == 0) { 119 return; 120 } 121 } while (errno == EINTR); 122 detectError(); 123 124 return; 125} 126 127llvm::raw_fd_ostream *OutputFile::dup() { 128 int newfd; 129 130 do { 131 newfd = ::dup(mFD); 132 if (newfd < 0) { 133 if (errno != EINTR) { 134 detectError(); 135 return NULL; 136 } 137 // EINTR 138 continue; 139 } 140 // dup() returns ok. 141 break; 142 } while (true); 143 144 llvm::raw_fd_ostream *result = 145 new (std::nothrow) llvm::raw_fd_ostream(newfd, /* shouldClose */true); 146 147 if (result == NULL) { 148 mError.assign(llvm::errc::not_enough_memory, llvm::system_category()); 149 } 150 151 return result; 152} 153