• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "net/disk_cache/simple/simple_file_tracker.h"
11 
12 #include <algorithm>
13 #include <limits>
14 #include <memory>
15 #include <utility>
16 
17 #include "base/files/file.h"
18 #include "base/logging.h"
19 #include "base/metrics/histogram_macros.h"
20 #include "base/not_fatal_until.h"
21 #include "base/synchronization/lock.h"
22 #include "net/disk_cache/disk_cache.h"
23 #include "net/disk_cache/simple/simple_histogram_enums.h"
24 #include "net/disk_cache/simple/simple_synchronous_entry.h"
25 
26 namespace disk_cache {
27 
28 namespace {
29 
RecordFileDescripterLimiterOp(FileDescriptorLimiterOp op)30 void RecordFileDescripterLimiterOp(FileDescriptorLimiterOp op) {
31   UMA_HISTOGRAM_ENUMERATION("SimpleCache.FileDescriptorLimiterAction", op,
32                             FD_LIMIT_OP_MAX);
33 }
34 
35 }  // namespace
36 
SimpleFileTracker(int file_limit)37 SimpleFileTracker::SimpleFileTracker(int file_limit)
38     : file_limit_(file_limit) {}
39 
~SimpleFileTracker()40 SimpleFileTracker::~SimpleFileTracker() {
41   DCHECK(lru_.empty());
42   DCHECK(tracked_files_.empty());
43 }
44 
Register(const SimpleSynchronousEntry * owner,SubFile subfile,std::unique_ptr<base::File> file)45 void SimpleFileTracker::Register(const SimpleSynchronousEntry* owner,
46                                  SubFile subfile,
47                                  std::unique_ptr<base::File> file) {
48   DCHECK(file->IsValid());
49   std::vector<std::unique_ptr<base::File>> files_to_close;
50 
51   {
52     base::AutoLock hold_lock(lock_);
53 
54     // Make sure the list of everything with given hash exists.
55     auto insert_status =
56         tracked_files_.emplace(owner->entry_file_key().entry_hash,
57                                std::vector<std::unique_ptr<TrackedFiles>>());
58 
59     std::vector<std::unique_ptr<TrackedFiles>>& candidates =
60         insert_status.first->second;
61 
62     // See if entry for |owner| already exists, if not append.
63     TrackedFiles* owners_files = nullptr;
64     for (const std::unique_ptr<TrackedFiles>& candidate : candidates) {
65       if (candidate->owner == owner) {
66         owners_files = candidate.get();
67         break;
68       }
69     }
70 
71     if (!owners_files) {
72       candidates.emplace_back(std::make_unique<TrackedFiles>());
73       owners_files = candidates.back().get();
74       owners_files->owner = owner;
75       owners_files->key = owner->entry_file_key();
76     }
77 
78     EnsureInFrontOfLRU(owners_files);
79 
80     int file_index = static_cast<int>(subfile);
81     DCHECK_EQ(TrackedFiles::TF_NO_REGISTRATION,
82               owners_files->state[file_index]);
83     owners_files->files[file_index] = std::move(file);
84     owners_files->state[file_index] = TrackedFiles::TF_REGISTERED;
85     ++open_files_;
86     CloseFilesIfTooManyOpen(&files_to_close);
87   }
88 }
89 
Acquire(BackendFileOperations * file_operations,const SimpleSynchronousEntry * owner,SubFile subfile)90 SimpleFileTracker::FileHandle SimpleFileTracker::Acquire(
91     BackendFileOperations* file_operations,
92     const SimpleSynchronousEntry* owner,
93     SubFile subfile) {
94   std::vector<std::unique_ptr<base::File>> files_to_close;
95 
96   {
97     base::AutoLock hold_lock(lock_);
98     TrackedFiles* owners_files = Find(owner);
99     int file_index = static_cast<int>(subfile);
100 
101     DCHECK_EQ(TrackedFiles::TF_REGISTERED, owners_files->state[file_index]);
102     owners_files->state[file_index] = TrackedFiles::TF_ACQUIRED;
103     EnsureInFrontOfLRU(owners_files);
104 
105     // Check to see if we have to reopen the file. That might push us over the
106     // fd limit.  CloseFilesIfTooManyOpen will not close anything in
107     // |*owners_files| since it's already in the the TF_ACQUIRED state.
108     if (owners_files->files[file_index] == nullptr) {
109       ReopenFile(file_operations, owners_files, subfile);
110       CloseFilesIfTooManyOpen(&files_to_close);
111     }
112 
113     return FileHandle(this, owner, subfile,
114                       owners_files->files[file_index].get());
115   }
116 }
117 
TrackedFiles()118 SimpleFileTracker::TrackedFiles::TrackedFiles() {
119   std::fill(state, state + kSimpleEntryTotalFileCount, TF_NO_REGISTRATION);
120 }
121 
122 SimpleFileTracker::TrackedFiles::~TrackedFiles() = default;
123 
Empty() const124 bool SimpleFileTracker::TrackedFiles::Empty() const {
125   for (State s : state)
126     if (s != TF_NO_REGISTRATION)
127       return false;
128   return true;
129 }
130 
HasOpenFiles() const131 bool SimpleFileTracker::TrackedFiles::HasOpenFiles() const {
132   for (const std::unique_ptr<base::File>& file : files)
133     if (file != nullptr)
134       return true;
135   return false;
136 }
137 
Release(const SimpleSynchronousEntry * owner,SubFile subfile)138 void SimpleFileTracker::Release(const SimpleSynchronousEntry* owner,
139                                 SubFile subfile) {
140   std::vector<std::unique_ptr<base::File>> files_to_close;
141 
142   {
143     base::AutoLock hold_lock(lock_);
144     TrackedFiles* owners_files = Find(owner);
145     int file_index = static_cast<int>(subfile);
146 
147     DCHECK(owners_files->state[file_index] == TrackedFiles::TF_ACQUIRED ||
148            owners_files->state[file_index] ==
149                TrackedFiles::TF_ACQUIRED_PENDING_CLOSE);
150 
151     // Prepare to executed deferred close, if any.
152     if (owners_files->state[file_index] ==
153         TrackedFiles::TF_ACQUIRED_PENDING_CLOSE) {
154       files_to_close.push_back(PrepareClose(owners_files, file_index));
155     } else {
156       owners_files->state[file_index] = TrackedFiles::TF_REGISTERED;
157     }
158 
159     // It's possible that we were over limit and couldn't do much about it
160     // since everything was lent out, so now may be the time to close extra
161     // stuff.
162     CloseFilesIfTooManyOpen(&files_to_close);
163   }
164 }
165 
Close(const SimpleSynchronousEntry * owner,SubFile subfile)166 void SimpleFileTracker::Close(const SimpleSynchronousEntry* owner,
167                               SubFile subfile) {
168   std::unique_ptr<base::File> file_to_close;
169 
170   {
171     base::AutoLock hold_lock(lock_);
172     TrackedFiles* owners_files = Find(owner);
173     int file_index = static_cast<int>(subfile);
174 
175     DCHECK(owners_files->state[file_index] == TrackedFiles::TF_ACQUIRED ||
176            owners_files->state[file_index] == TrackedFiles::TF_REGISTERED);
177 
178     if (owners_files->state[file_index] == TrackedFiles::TF_ACQUIRED) {
179       // The FD is currently acquired, so we can't clean up the TrackedFiles,
180       // just yet; even if this is the last close, so delay the close until it
181       // gets released.
182       owners_files->state[file_index] = TrackedFiles::TF_ACQUIRED_PENDING_CLOSE;
183     } else {
184       file_to_close = PrepareClose(owners_files, file_index);
185     }
186   }
187 }
188 
Doom(const SimpleSynchronousEntry * owner,EntryFileKey * key)189 void SimpleFileTracker::Doom(const SimpleSynchronousEntry* owner,
190                              EntryFileKey* key) {
191   base::AutoLock hold_lock(lock_);
192   auto iter = tracked_files_.find(key->entry_hash);
193   CHECK(iter != tracked_files_.end(), base::NotFatalUntil::M130);
194 
195   uint64_t max_doom_gen = 0;
196   for (const std::unique_ptr<TrackedFiles>& file_with_same_hash :
197        iter->second) {
198     max_doom_gen =
199         std::max(max_doom_gen, file_with_same_hash->key.doom_generation);
200   }
201 
202   // It would take >502 years to doom the same hash enough times (at 10^9 dooms
203   // per second) to wrap the 64 bit counter. Still, if it does wrap around,
204   // there is a security risk since we could confuse different keys.
205   CHECK_NE(max_doom_gen, std::numeric_limits<uint64_t>::max());
206   uint64_t new_doom_gen = max_doom_gen + 1;
207 
208   // Update external key.
209   key->doom_generation = new_doom_gen;
210 
211   // Update our own.
212   for (const std::unique_ptr<TrackedFiles>& file_with_same_hash :
213        iter->second) {
214     if (file_with_same_hash->owner == owner)
215       file_with_same_hash->key.doom_generation = new_doom_gen;
216   }
217 }
218 
IsEmptyForTesting()219 bool SimpleFileTracker::IsEmptyForTesting() {
220   base::AutoLock hold_lock(lock_);
221   return tracked_files_.empty() && lru_.empty();
222 }
223 
Find(const SimpleSynchronousEntry * owner)224 SimpleFileTracker::TrackedFiles* SimpleFileTracker::Find(
225     const SimpleSynchronousEntry* owner) {
226   auto candidates = tracked_files_.find(owner->entry_file_key().entry_hash);
227   CHECK(candidates != tracked_files_.end(), base::NotFatalUntil::M130);
228   for (const auto& candidate : candidates->second) {
229     if (candidate->owner == owner) {
230       return candidate.get();
231     }
232   }
233   LOG(DFATAL) << "SimpleFileTracker operation on non-found entry";
234   return nullptr;
235 }
236 
PrepareClose(TrackedFiles * owners_files,int file_index)237 std::unique_ptr<base::File> SimpleFileTracker::PrepareClose(
238     TrackedFiles* owners_files,
239     int file_index) {
240   std::unique_ptr<base::File> file_out =
241       std::move(owners_files->files[file_index]);
242   owners_files->state[file_index] = TrackedFiles::TF_NO_REGISTRATION;
243   if (owners_files->Empty()) {
244     auto iter = tracked_files_.find(owners_files->key.entry_hash);
245     for (auto i = iter->second.begin(); i != iter->second.end(); ++i) {
246       if ((*i).get() == owners_files) {
247         if (owners_files->in_lru)
248           lru_.erase(owners_files->position_in_lru);
249         iter->second.erase(i);
250         break;
251       }
252     }
253     if (iter->second.empty())
254       tracked_files_.erase(iter);
255   }
256   if (file_out != nullptr)
257     --open_files_;
258   return file_out;
259 }
260 
CloseFilesIfTooManyOpen(std::vector<std::unique_ptr<base::File>> * files_to_close)261 void SimpleFileTracker::CloseFilesIfTooManyOpen(
262     std::vector<std::unique_ptr<base::File>>* files_to_close) {
263   auto i = lru_.end();
264   while (open_files_ > file_limit_ && i != lru_.begin()) {
265     --i;  // Point to the actual entry.
266     TrackedFiles* tracked_files = *i;
267     DCHECK(tracked_files->in_lru);
268     for (int j = 0; j < kSimpleEntryTotalFileCount; ++j) {
269       if (tracked_files->state[j] == TrackedFiles::TF_REGISTERED &&
270           tracked_files->files[j] != nullptr) {
271         files_to_close->push_back(std::move(tracked_files->files[j]));
272         --open_files_;
273         RecordFileDescripterLimiterOp(FD_LIMIT_CLOSE_FILE);
274       }
275     }
276 
277     if (!tracked_files->HasOpenFiles()) {
278       // If there is nothing here that can possibly be closed, remove this from
279       // LRU for now so we don't have to rescan it next time we are here. If the
280       // files get re-opened (in Acquire), it will get added back in.
281       DCHECK_EQ(*tracked_files->position_in_lru, tracked_files);
282       DCHECK(i == tracked_files->position_in_lru);
283       // Note that we're erasing at i, which would make it invalid, so go back
284       // one element ahead to we can decrement from that on next iteration.
285       ++i;
286       lru_.erase(tracked_files->position_in_lru);
287       tracked_files->in_lru = false;
288     }
289   }
290 }
291 
ReopenFile(BackendFileOperations * file_operations,TrackedFiles * owners_files,SubFile subfile)292 void SimpleFileTracker::ReopenFile(BackendFileOperations* file_operations,
293                                    TrackedFiles* owners_files,
294                                    SubFile subfile) {
295   int file_index = static_cast<int>(subfile);
296   DCHECK(owners_files->files[file_index] == nullptr);
297   int flags = base::File::FLAG_OPEN | base::File::FLAG_READ |
298               base::File::FLAG_WRITE | base::File::FLAG_WIN_SHARE_DELETE;
299   base::FilePath file_path =
300       owners_files->owner->GetFilenameForSubfile(subfile);
301   owners_files->files[file_index] =
302       std::make_unique<base::File>(file_operations->OpenFile(file_path, flags));
303   if (owners_files->files[file_index]->IsValid()) {
304     RecordFileDescripterLimiterOp(FD_LIMIT_REOPEN_FILE);
305 
306     ++open_files_;
307   } else {
308     owners_files->files[file_index] = nullptr;
309     RecordFileDescripterLimiterOp(FD_LIMIT_FAIL_REOPEN_FILE);
310   }
311 }
312 
EnsureInFrontOfLRU(TrackedFiles * owners_files)313 void SimpleFileTracker::EnsureInFrontOfLRU(TrackedFiles* owners_files) {
314   if (!owners_files->in_lru) {
315     lru_.push_front(owners_files);
316     owners_files->position_in_lru = lru_.begin();
317     owners_files->in_lru = true;
318   } else if (owners_files->position_in_lru != lru_.begin()) {
319     lru_.splice(lru_.begin(), lru_, owners_files->position_in_lru);
320   }
321   DCHECK_EQ(*owners_files->position_in_lru, owners_files);
322 }
323 
324 SimpleFileTracker::FileHandle::FileHandle() = default;
325 
FileHandle(SimpleFileTracker * file_tracker,const SimpleSynchronousEntry * entry,SimpleFileTracker::SubFile subfile,base::File * file)326 SimpleFileTracker::FileHandle::FileHandle(SimpleFileTracker* file_tracker,
327                                           const SimpleSynchronousEntry* entry,
328                                           SimpleFileTracker::SubFile subfile,
329                                           base::File* file)
330     : file_tracker_(file_tracker),
331       entry_(entry),
332       subfile_(subfile),
333       file_(file) {}
334 
FileHandle(FileHandle && other)335 SimpleFileTracker::FileHandle::FileHandle(FileHandle&& other) {
336   *this = std::move(other);
337 }
338 
~FileHandle()339 SimpleFileTracker::FileHandle::~FileHandle() {
340   file_ = nullptr;
341   if (entry_) {
342     file_tracker_->Release(entry_.ExtractAsDangling(), subfile_);
343   }
344 }
345 
operator =(FileHandle && other)346 SimpleFileTracker::FileHandle& SimpleFileTracker::FileHandle::operator=(
347     FileHandle&& other) {
348   file_tracker_ = other.file_tracker_;
349   entry_ = other.entry_;
350   subfile_ = other.subfile_;
351   file_ = other.file_;
352   other.file_tracker_ = nullptr;
353   other.entry_ = nullptr;
354   other.file_ = nullptr;
355   return *this;
356 }
357 
operator ->() const358 base::File* SimpleFileTracker::FileHandle::operator->() const {
359   return file_;
360 }
361 
get() const362 base::File* SimpleFileTracker::FileHandle::get() const {
363   return file_;
364 }
365 
IsOK() const366 bool SimpleFileTracker::FileHandle::IsOK() const {
367   return file_ && file_->IsValid();
368 }
369 
370 }  // namespace disk_cache
371