87 static absl::StatusOr<std::unique_ptr<SourceArtifactWriteLocks>>
Acquire(
88 const std::vector<fs::path>& lock_paths,
90 if (lock_paths.empty()) {
91 return absl::InvalidArgumentError(
92 absl::StrFormat(
"%s publication requires at least one lock",
95 auto lock = std::unique_ptr<SourceArtifactWriteLocks>(
98 lock->process_lock_acquired_ =
true;
101 lock->handles_.reserve(lock_paths.size());
102 struct WindowsFileIdentity {
104 DWORD file_index_high;
105 DWORD file_index_low;
107 std::vector<WindowsFileIdentity> lock_identities;
108 lock_identities.reserve(lock_paths.size());
109 for (
const fs::path& path : lock_paths) {
110 HANDLE handle = CreateFileW(
111 path.wstring().c_str(), GENERIC_READ | GENERIC_WRITE,
112 FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr, OPEN_ALWAYS,
113 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OPEN_REPARSE_POINT,
nullptr);
114 if (handle == INVALID_HANDLE_VALUE) {
115 const DWORD error = GetLastError();
116 return absl::PermissionDeniedError(absl::StrFormat(
117 "Cannot open %s lock %s: %s", labels.
subject, path.string(),
118 std::error_code(
static_cast<int>(error), std::system_category())
121 BY_HANDLE_FILE_INFORMATION file_info = {};
122 if (!GetFileInformationByHandle(handle, &file_info)) {
123 const DWORD error = GetLastError();
125 return absl::FailedPreconditionError(absl::StrFormat(
126 "Cannot inspect %s lock %s: %s", labels.
subject, path.string(),
127 std::error_code(
static_cast<int>(error), std::system_category())
130 if ((file_info.dwFileAttributes &
131 (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY)) != 0) {
133 return absl::FailedPreconditionError(
134 absl::StrFormat(
"%s lock must be a regular file: %s",
137 if (file_info.nNumberOfLinks != 1) {
139 return absl::FailedPreconditionError(
140 absl::StrFormat(
"%s lock must have exactly one hard link: %s",
143 const WindowsFileIdentity identity = {file_info.dwVolumeSerialNumber,
144 file_info.nFileIndexHigh,
145 file_info.nFileIndexLow};
146 const bool duplicate_identity = std::any_of(
147 lock_identities.begin(), lock_identities.end(),
148 [&](
const WindowsFileIdentity& existing) {
149 return existing.volume_serial == identity.volume_serial &&
150 existing.file_index_high == identity.file_index_high &&
151 existing.file_index_low == identity.file_index_low;
153 if (duplicate_identity) {
155 return absl::InvalidArgumentError(
156 absl::StrFormat(
"%s lock aliases another lock path: %s",
159 OVERLAPPED overlapped = {};
160 if (!LockFileEx(handle, LOCKFILE_EXCLUSIVE_LOCK, 0, MAXDWORD, MAXDWORD,
162 const DWORD error = GetLastError();
164 return absl::UnavailableError(absl::StrFormat(
165 "Cannot acquire %s lock %s: %s", labels.
subject, path.string(),
166 std::error_code(
static_cast<int>(error), std::system_category())
169 lock_identities.push_back(identity);
170 lock->handles_.push_back(handle);
172#elif defined(__EMSCRIPTEN__)
173 return absl::FailedPreconditionError(absl::StrFormat(
174 "Durable %s locking is unavailable in browser builds", labels.
subject));
176 lock->fds_.reserve(lock_paths.size());
177 struct PosixFileIdentity {
181 std::vector<PosixFileIdentity> lock_identities;
182 lock_identities.reserve(lock_paths.size());
183 for (
const fs::path& path : lock_paths) {
184 int open_flags = O_RDWR | O_CREAT;
186 open_flags |= O_CLOEXEC;
189 open_flags |= O_NOFOLLOW;
191 const int fd = open(path.c_str(), open_flags, S_IRUSR | S_IWUSR);
193 return absl::PermissionDeniedError(absl::StrFormat(
194 "Cannot open %s lock %s: %s", labels.
subject, path.string(),
195 std::error_code(errno, std::generic_category()).message()));
197 struct stat lock_stat{};
198 if (fstat(fd, &lock_stat) != 0) {
199 const int error = errno;
201 return absl::FailedPreconditionError(absl::StrFormat(
202 "Cannot inspect %s lock %s: %s", labels.
subject, path.string(),
203 std::error_code(error, std::generic_category()).message()));
205 if (!S_ISREG(lock_stat.st_mode)) {
207 return absl::FailedPreconditionError(
208 absl::StrFormat(
"%s lock must be a regular file: %s",
211 if (lock_stat.st_nlink != 1) {
213 return absl::FailedPreconditionError(
214 absl::StrFormat(
"%s lock must have exactly one hard link: %s",
217 const PosixFileIdentity identity = {lock_stat.st_dev, lock_stat.st_ino};
218 const bool duplicate_identity =
219 std::any_of(lock_identities.begin(), lock_identities.end(),
220 [&](
const PosixFileIdentity& existing) {
221 return existing.device == identity.device &&
222 existing.inode == identity.inode;
224 if (duplicate_identity) {
226 return absl::InvalidArgumentError(
227 absl::StrFormat(
"%s lock aliases another lock path: %s",
230 if (fchmod(fd, S_IRUSR | S_IWUSR) != 0) {
231 const int error = errno;
233 return absl::PermissionDeniedError(absl::StrFormat(
234 "Cannot secure %s lock %s: %s", labels.
subject, path.string(),
235 std::error_code(error, std::generic_category()).message()));
237 while (flock(fd, LOCK_EX) != 0) {
238 if (errno == EINTR) {
241 const int error = errno;
243 return absl::UnavailableError(absl::StrFormat(
244 "Cannot acquire %s lock %s: %s", labels.
subject, path.string(),
245 std::error_code(error, std::generic_category()).message()));
247 lock_identities.push_back(identity);
248 lock->fds_.push_back(fd);