1/* 2 * Copyright (C) 2011 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 "scoped_flock.h" 18 19#include <sys/file.h> 20#include <sys/stat.h> 21 22#include "base/logging.h" 23#include "base/stringprintf.h" 24#include "base/unix_file/fd_file.h" 25 26namespace art { 27 28bool ScopedFlock::Init(const char* filename, std::string* error_msg) { 29 return Init(filename, O_CREAT | O_RDWR, true, error_msg); 30} 31 32bool ScopedFlock::Init(const char* filename, int flags, bool block, std::string* error_msg) { 33 while (true) { 34 if (file_.get() != nullptr) { 35 UNUSED(file_->FlushCloseOrErase()); // Ignore result. 36 } 37 file_.reset(OS::OpenFileWithFlags(filename, flags)); 38 if (file_.get() == nullptr) { 39 *error_msg = StringPrintf("Failed to open file '%s': %s", filename, strerror(errno)); 40 return false; 41 } 42 int operation = block ? LOCK_EX : (LOCK_EX | LOCK_NB); 43 int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), operation)); 44 if (flock_result == EWOULDBLOCK) { 45 // File is locked by someone else and we are required not to block; 46 return false; 47 } 48 if (flock_result != 0) { 49 *error_msg = StringPrintf("Failed to lock file '%s': %s", filename, strerror(errno)); 50 return false; 51 } 52 struct stat fstat_stat; 53 int fstat_result = TEMP_FAILURE_RETRY(fstat(file_->Fd(), &fstat_stat)); 54 if (fstat_result != 0) { 55 *error_msg = StringPrintf("Failed to fstat file '%s': %s", filename, strerror(errno)); 56 return false; 57 } 58 struct stat stat_stat; 59 int stat_result = TEMP_FAILURE_RETRY(stat(filename, &stat_stat)); 60 if (stat_result != 0) { 61 PLOG(WARNING) << "Failed to stat, will retry: " << filename; 62 // ENOENT can happen if someone racing with us unlinks the file we created so just retry. 63 if (block) { 64 continue; 65 } else { 66 // Note that in theory we could race with someone here for a long time and end up retrying 67 // over and over again. This potential behavior does not fit well in the non-blocking 68 // semantics. Thus, if we are not require to block return failure when racing. 69 return false; 70 } 71 } 72 if (fstat_stat.st_dev != stat_stat.st_dev || fstat_stat.st_ino != stat_stat.st_ino) { 73 LOG(WARNING) << "File changed while locking, will retry: " << filename; 74 if (block) { 75 continue; 76 } else { 77 // See comment above. 78 return false; 79 } 80 } 81 return true; 82 } 83} 84 85bool ScopedFlock::Init(File* file, std::string* error_msg) { 86 file_.reset(new File(dup(file->Fd()), file->GetPath(), file->CheckUsage(), file->ReadOnlyMode())); 87 if (file_->Fd() == -1) { 88 file_.reset(); 89 *error_msg = StringPrintf("Failed to duplicate open file '%s': %s", 90 file->GetPath().c_str(), strerror(errno)); 91 return false; 92 } 93 if (0 != TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_EX))) { 94 file_.reset(); 95 *error_msg = StringPrintf( 96 "Failed to lock file '%s': %s", file->GetPath().c_str(), strerror(errno)); 97 return false; 98 } 99 return true; 100} 101 102File* ScopedFlock::GetFile() const { 103 CHECK(file_.get() != nullptr); 104 return file_.get(); 105} 106 107bool ScopedFlock::HasFile() { 108 return file_.get() != nullptr; 109} 110 111ScopedFlock::ScopedFlock() { } 112 113ScopedFlock::~ScopedFlock() { 114 if (file_.get() != nullptr) { 115 int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_UN)); 116 CHECK_EQ(0, flock_result); 117 int close_result = -1; 118 if (file_->ReadOnlyMode()) { 119 close_result = file_->Close(); 120 } else { 121 close_result = file_->FlushCloseOrErase(); 122 } 123 if (close_result != 0) { 124 PLOG(WARNING) << "Could not close scoped file lock file."; 125 } 126 } 127} 128 129} // namespace art 130