1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "preferences_file_lock.h"
17 #include "preferences_file_operation.h"
18 
19 #include <fcntl.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 
23 #include <cerrno>
24 #include <cstdio>
25 #include <thread>
26 
27 #include "log_print.h"
28 #include "visibility.h"
29 namespace OHOS {
30 namespace NativePreferences {
31 std::map<std::string, std::shared_ptr<std::mutex>> PreferencesLockManager::inProcessMutexs_;
32 std::mutex PreferencesLockManager::mapMutex_;
33 
Get(const std::string fileName)34 std::shared_ptr<std::mutex> PreferencesLockManager::Get(const std::string fileName)
35 {
36     std::lock_guard<std::mutex> lockMutex(mapMutex_);
37     auto iter = inProcessMutexs_.find(fileName);
38     if (iter != inProcessMutexs_.end()) {
39         return iter->second;
40     }
41     auto res = inProcessMutexs_.insert_or_assign(fileName, std::make_shared<std::mutex>());
42     return res.first->second;
43 }
44 
45 #if !defined(WINDOWS_PLATFORM)
46 static const std::chrono::microseconds WAIT_CONNECT_TIMEOUT(20);
47 static const int ATTEMPTS = 5;
PreferencesFileLock(const std::string & path,const std::string & dataGroupId)48 PreferencesFileLock::PreferencesFileLock(const std::string &path, const std::string &dataGroupId)
49     : inProcessMutex_(PreferencesLockManager::Get(path))
50 {
51     inProcessMutex_->lock();
52     if (dataGroupId.empty()) {
53         return;
54     }
55     fd_ = open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
56     if (fd_ == -1) {
57         LOG_ERROR("Couldn't open file %{public}s errno %{public}d.", ExtractFileName(path).c_str(), errno);
58         return;
59     }
60     struct flock fileLockInfo = { 0 };
61     fileLockInfo.l_type = F_WRLCK;
62     fileLockInfo.l_whence = SEEK_SET;
63     fileLockInfo.l_start = 0;
64     fileLockInfo.l_len = 0;
65 
66     for (size_t i = 0; i < ATTEMPTS; ++i) {
67         if (fcntl(fd_, F_SETLK, &fileLockInfo) != -1) {
68             LOG_DEBUG("successfully obtained file lock");
69             return;
70         }
71         LOG_DEBUG("Attempt to obtain file lock again %{public}d", errno);
72         std::this_thread::sleep_for(WAIT_CONNECT_TIMEOUT);
73     }
74     LOG_ERROR("attempt to lock file %{public}s failed. Please try again", ExtractFileName(path).c_str());
75 }
76 
~PreferencesFileLock()77 PreferencesFileLock::~PreferencesFileLock()
78 {
79     inProcessMutex_->unlock();
80     if (fd_ > 0) {
81         struct flock fileLockInfo = { 0 };
82         fileLockInfo.l_type = F_UNLCK;
83         fileLockInfo.l_whence = SEEK_SET;
84         fileLockInfo.l_start = 0;
85         fileLockInfo.l_len = 0;
86         if (fcntl(fd_, F_SETLK, &fileLockInfo) == -1) {
87             LOG_ERROR("failed to release file lock error %{public}d.", errno);
88         }
89         close(fd_);
90         fd_ = -1;
91     }
92 }
93 
94 #else
95 
PreferencesFileLock(const std::string & path,const std::string & dataGroupId)96 PreferencesFileLock::PreferencesFileLock(const std::string &path, const std::string &dataGroupId)
97 {
98     fd_ = -1;
99     inProcessMutex_.reset();
100 }
101 
~PreferencesFileLock()102 PreferencesFileLock::~PreferencesFileLock()
103 {
104 }
105 
106 #endif
107 } // End of namespace NativePreferences
108 } // End of namespace OHOS