1d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath/*
2d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath * Copyright (C) 2011 The Android Open Source Project
3d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath *
4d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath * Licensed under the Apache License, Version 2.0 (the "License");
5d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath * you may not use this file except in compliance with the License.
6d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath * You may obtain a copy of the License at
7d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath *
8d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath *      http://www.apache.org/licenses/LICENSE-2.0
9d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath *
10d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath * Unless required by applicable law or agreed to in writing, software
11d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath * distributed under the License is distributed on an "AS IS" BASIS,
12d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath * See the License for the specific language governing permissions and
14d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath * limitations under the License.
15d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath */
16d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath
17d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath#include "scoped_flock.h"
18d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath
19d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath#include <sys/file.h>
20d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath#include <sys/stat.h>
21d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath
22d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath#include "base/logging.h"
23d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath#include "base/stringprintf.h"
24d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath#include "base/unix_file/fd_file.h"
25d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath
26d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamathnamespace art {
27d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath
28d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamathbool ScopedFlock::Init(const char* filename, std::string* error_msg) {
29877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle  return Init(filename, O_CREAT | O_RDWR, true, error_msg);
30877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle}
31877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle
32877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravlebool ScopedFlock::Init(const char* filename, int flags, bool block, std::string* error_msg) {
33d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath  while (true) {
344303ba97313458491e038d78efa041d41cf7bb43Andreas Gampe    if (file_.get() != nullptr) {
354303ba97313458491e038d78efa041d41cf7bb43Andreas Gampe      UNUSED(file_->FlushCloseOrErase());  // Ignore result.
364303ba97313458491e038d78efa041d41cf7bb43Andreas Gampe    }
37877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle    file_.reset(OS::OpenFileWithFlags(filename, flags));
382cebb24bfc3247d3e9be138a3350106737455918Mathieu Chartier    if (file_.get() == nullptr) {
39d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath      *error_msg = StringPrintf("Failed to open file '%s': %s", filename, strerror(errno));
40d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath      return false;
41d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath    }
42877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle    int operation = block ? LOCK_EX : (LOCK_EX | LOCK_NB);
43877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle    int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), operation));
44877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle    if (flock_result == EWOULDBLOCK) {
45877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle      // File is locked by someone else and we are required not to block;
46877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle      return false;
47877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle    }
48d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath    if (flock_result != 0) {
49d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath      *error_msg = StringPrintf("Failed to lock file '%s': %s", filename, strerror(errno));
50d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath      return false;
51d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath    }
52d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath    struct stat fstat_stat;
53d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath    int fstat_result = TEMP_FAILURE_RETRY(fstat(file_->Fd(), &fstat_stat));
54d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath    if (fstat_result != 0) {
55d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath      *error_msg = StringPrintf("Failed to fstat file '%s': %s", filename, strerror(errno));
56d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath      return false;
57d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath    }
58d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath    struct stat stat_stat;
59d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath    int stat_result = TEMP_FAILURE_RETRY(stat(filename, &stat_stat));
60d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath    if (stat_result != 0) {
61d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath      PLOG(WARNING) << "Failed to stat, will retry: " << filename;
62d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath      // ENOENT can happen if someone racing with us unlinks the file we created so just retry.
63877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle      if (block) {
64877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle        continue;
65877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle      } else {
66877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle        // Note that in theory we could race with someone here for a long time and end up retrying
67877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle        // over and over again. This potential behavior does not fit well in the non-blocking
68877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle        // semantics. Thus, if we are not require to block return failure when racing.
69877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle        return false;
70877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle      }
71d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath    }
72d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath    if (fstat_stat.st_dev != stat_stat.st_dev || fstat_stat.st_ino != stat_stat.st_ino) {
73d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath      LOG(WARNING) << "File changed while locking, will retry: " << filename;
74877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle      if (block) {
75877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle        continue;
76877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle      } else {
77877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle        // See comment above.
78877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle        return false;
79877fd963548a3175665bfef25b0d24bc0e5a0135Calin Juravle      }
80d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath    }
81d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath    return true;
82d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath  }
83d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath}
84d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath
85a59dd80f9f48cb750d329d4d4af2d99d72b484d1Alex Lightbool ScopedFlock::Init(File* file, std::string* error_msg) {
86024160850fbbf28368eae951beb4c72e2ce8fce6Calin Juravle  file_.reset(new File(dup(file->Fd()), file->GetPath(), file->CheckUsage(), file->ReadOnlyMode()));
87a59dd80f9f48cb750d329d4d4af2d99d72b484d1Alex Light  if (file_->Fd() == -1) {
88a59dd80f9f48cb750d329d4d4af2d99d72b484d1Alex Light    file_.reset();
89a59dd80f9f48cb750d329d4d4af2d99d72b484d1Alex Light    *error_msg = StringPrintf("Failed to duplicate open file '%s': %s",
90a59dd80f9f48cb750d329d4d4af2d99d72b484d1Alex Light                              file->GetPath().c_str(), strerror(errno));
91a59dd80f9f48cb750d329d4d4af2d99d72b484d1Alex Light    return false;
92a59dd80f9f48cb750d329d4d4af2d99d72b484d1Alex Light  }
93a59dd80f9f48cb750d329d4d4af2d99d72b484d1Alex Light  if (0 != TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_EX))) {
94a59dd80f9f48cb750d329d4d4af2d99d72b484d1Alex Light    file_.reset();
952cebb24bfc3247d3e9be138a3350106737455918Mathieu Chartier    *error_msg = StringPrintf(
962cebb24bfc3247d3e9be138a3350106737455918Mathieu Chartier        "Failed to lock file '%s': %s", file->GetPath().c_str(), strerror(errno));
97a59dd80f9f48cb750d329d4d4af2d99d72b484d1Alex Light    return false;
98a59dd80f9f48cb750d329d4d4af2d99d72b484d1Alex Light  }
99a59dd80f9f48cb750d329d4d4af2d99d72b484d1Alex Light  return true;
100a59dd80f9f48cb750d329d4d4af2d99d72b484d1Alex Light}
101a59dd80f9f48cb750d329d4d4af2d99d72b484d1Alex Light
102877fd963548a3175665bfef25b0d24bc0e5a0135Calin JuravleFile* ScopedFlock::GetFile() const {
1032cebb24bfc3247d3e9be138a3350106737455918Mathieu Chartier  CHECK(file_.get() != nullptr);
104d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath  return file_.get();
105d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath}
106d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath
107833a48501d560c9fa7fc78ef619888138c2d374fAndreas Gampebool ScopedFlock::HasFile() {
108833a48501d560c9fa7fc78ef619888138c2d374fAndreas Gampe  return file_.get() != nullptr;
109833a48501d560c9fa7fc78ef619888138c2d374fAndreas Gampe}
110833a48501d560c9fa7fc78ef619888138c2d374fAndreas Gampe
111d1c606f280797be81e2592c483869a6ec836a9f3Narayan KamathScopedFlock::ScopedFlock() { }
112d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath
113d1c606f280797be81e2592c483869a6ec836a9f3Narayan KamathScopedFlock::~ScopedFlock() {
1142cebb24bfc3247d3e9be138a3350106737455918Mathieu Chartier  if (file_.get() != nullptr) {
115d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath    int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_UN));
116d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath    CHECK_EQ(0, flock_result);
117024160850fbbf28368eae951beb4c72e2ce8fce6Calin Juravle    int close_result = -1;
118024160850fbbf28368eae951beb4c72e2ce8fce6Calin Juravle    if (file_->ReadOnlyMode()) {
119024160850fbbf28368eae951beb4c72e2ce8fce6Calin Juravle      close_result = file_->Close();
120024160850fbbf28368eae951beb4c72e2ce8fce6Calin Juravle    } else {
121024160850fbbf28368eae951beb4c72e2ce8fce6Calin Juravle      close_result = file_->FlushCloseOrErase();
122024160850fbbf28368eae951beb4c72e2ce8fce6Calin Juravle    }
123024160850fbbf28368eae951beb4c72e2ce8fce6Calin Juravle    if (close_result != 0) {
1244303ba97313458491e038d78efa041d41cf7bb43Andreas Gampe      PLOG(WARNING) << "Could not close scoped file lock file.";
1254303ba97313458491e038d78efa041d41cf7bb43Andreas Gampe    }
126d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath  }
127d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath}
128d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath
129d1c606f280797be81e2592c483869a6ec836a9f3Narayan Kamath}  // namespace art
130