1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "sql/vfs_wrapper_fuchsia.h"
6 
7 #include "base/containers/flat_set.h"
8 #include "base/no_destructor.h"
9 #include "base/synchronization/lock.h"
10 #include "base/thread_annotations.h"
11 #include "sql/vfs_wrapper.h"
12 
13 namespace sql {
14 
15 namespace {
16 
17 // Singleton that stores locks state.
18 class FuchsiaFileLockManager {
19  public:
20   FuchsiaFileLockManager() = default;
21 
22   // Returns lock manager for the current process.
Instance()23   static FuchsiaFileLockManager* Instance() {
24     static base::NoDestructor<FuchsiaFileLockManager> lock_manager;
25     return lock_manager.get();
26   }
27 
28   // Return true if the file was locked successfully.
Lock(const std::string & name)29   bool Lock(const std::string& name) {
30     base::AutoLock lock(lock_);
31 
32     if (locked_files_.find(name) != locked_files_.end()) {
33       DLOG(WARNING) << "File " << name
34                     << " is being used concurrently by multiple consumers.";
35       return false;
36     }
37 
38     locked_files_.insert(name);
39     return true;
40   }
41 
Unlock(const std::string & name)42   void Unlock(const std::string& name) {
43     base::AutoLock lock(lock_);
44 
45     size_t removed = locked_files_.erase(name);
46     DCHECK_EQ(removed, 1U);
47   }
48 
IsLocked(const std::string & name)49   bool IsLocked(const std::string& name) {
50     base::AutoLock lock(lock_);
51     return locked_files_.find(name) != locked_files_.end();
52   }
53 
54  private:
55   ~FuchsiaFileLockManager() = delete;
56 
57   base::Lock lock_;
58 
59   // Set of all currently locked files.
60   base::flat_set<std::string> locked_files_ GUARDED_BY(lock_);
61 };
62 
63 }  // namespace
64 
FuchsiaVfsLock(sqlite3_file * sqlite_file,int file_lock)65 int FuchsiaVfsLock(sqlite3_file* sqlite_file, int file_lock) {
66   DCHECK(file_lock == SQLITE_LOCK_SHARED || file_lock == SQLITE_LOCK_RESERVED ||
67          file_lock == SQLITE_LOCK_PENDING ||
68          file_lock == SQLITE_LOCK_EXCLUSIVE);
69 
70   VfsFile* vfs_file = reinterpret_cast<VfsFile*>(sqlite_file);
71 
72   if (vfs_file->lock_level == SQLITE_LOCK_NONE) {
73     if (!FuchsiaFileLockManager::Instance()->Lock(vfs_file->file_name))
74       return SQLITE_BUSY;
75   }
76 
77   vfs_file->lock_level = file_lock;
78 
79   return SQLITE_OK;
80 }
81 
FuchsiaVfsUnlock(sqlite3_file * sqlite_file,int file_lock)82 int FuchsiaVfsUnlock(sqlite3_file* sqlite_file, int file_lock) {
83   VfsFile* vfs_file = reinterpret_cast<VfsFile*>(sqlite_file);
84 
85   if (file_lock == SQLITE_LOCK_NONE) {
86     if (vfs_file->lock_level != SQLITE_LOCK_NONE)
87       FuchsiaFileLockManager::Instance()->Unlock(vfs_file->file_name);
88   } else {
89     // Keep the file locked for the shared lock.
90     DCHECK(file_lock == SQLITE_LOCK_SHARED);
91     DCHECK(FuchsiaFileLockManager::Instance()->IsLocked(vfs_file->file_name));
92   }
93   vfs_file->lock_level = file_lock;
94 
95   return SQLITE_OK;
96 }
97 
FuchsiaVfsCheckReservedLock(sqlite3_file * sqlite_file,int * result)98 int FuchsiaVfsCheckReservedLock(sqlite3_file* sqlite_file, int* result) {
99   VfsFile* vfs_file = reinterpret_cast<VfsFile*>(sqlite_file);
100   return vfs_file->lock_level;
101 }
102 
103 }  // namespace sql
104