72 static absl::StatusOr<std::unique_ptr<SourceArtifactWriteLocks>>
Acquire(
73 const std::vector<fs::path>& lock_paths,
75 if (lock_paths.empty()) {
76 return absl::InvalidArgumentError(
77 absl::StrFormat(
"%s publication requires at least one lock",
80 auto lock = std::unique_ptr<SourceArtifactWriteLocks>(
83 lock->process_lock_acquired_ =
true;
86 lock->handles_.reserve(lock_paths.size());
87 struct WindowsFileIdentity {
89 DWORD file_index_high;
92 std::vector<WindowsFileIdentity> lock_identities;
93 lock_identities.reserve(lock_paths.size());
94 for (
const fs::path& path : lock_paths) {
95 HANDLE handle = CreateFileW(
96 path.wstring().c_str(), GENERIC_READ | GENERIC_WRITE,
97 FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr, OPEN_ALWAYS,
98 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OPEN_REPARSE_POINT,
nullptr);
99 if (handle == INVALID_HANDLE_VALUE) {
100 const DWORD error = GetLastError();
101 return absl::PermissionDeniedError(absl::StrFormat(
102 "Cannot open %s lock %s: %s", labels.
subject, path.string(),
103 std::error_code(
static_cast<int>(error), std::system_category())
106 BY_HANDLE_FILE_INFORMATION file_info = {};
107 if (!GetFileInformationByHandle(handle, &file_info)) {
108 const DWORD error = GetLastError();
110 return absl::FailedPreconditionError(absl::StrFormat(
111 "Cannot inspect %s lock %s: %s", labels.
subject, path.string(),
112 std::error_code(
static_cast<int>(error), std::system_category())
115 if ((file_info.dwFileAttributes &
116 (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY)) != 0) {
118 return absl::FailedPreconditionError(
119 absl::StrFormat(
"%s lock must be a regular file: %s",
122 if (file_info.nNumberOfLinks != 1) {
124 return absl::FailedPreconditionError(
125 absl::StrFormat(
"%s lock must have exactly one hard link: %s",
128 const WindowsFileIdentity identity = {file_info.dwVolumeSerialNumber,
129 file_info.nFileIndexHigh,
130 file_info.nFileIndexLow};
131 const bool duplicate_identity = std::any_of(
132 lock_identities.begin(), lock_identities.end(),
133 [&](
const WindowsFileIdentity& existing) {
134 return existing.volume_serial == identity.volume_serial &&
135 existing.file_index_high == identity.file_index_high &&
136 existing.file_index_low == identity.file_index_low;
138 if (duplicate_identity) {
140 return absl::InvalidArgumentError(
141 absl::StrFormat(
"%s lock aliases another lock path: %s",
144 OVERLAPPED overlapped = {};
145 if (!LockFileEx(handle, LOCKFILE_EXCLUSIVE_LOCK, 0, MAXDWORD, MAXDWORD,
147 const DWORD error = GetLastError();
149 return absl::UnavailableError(absl::StrFormat(
150 "Cannot acquire %s lock %s: %s", labels.
subject, path.string(),
151 std::error_code(
static_cast<int>(error), std::system_category())
154 lock_identities.push_back(identity);
155 lock->handles_.push_back(handle);
157#elif defined(__EMSCRIPTEN__)
158 return absl::FailedPreconditionError(absl::StrFormat(
159 "Durable %s locking is unavailable in browser builds", labels.
subject));
161 lock->fds_.reserve(lock_paths.size());
162 struct PosixFileIdentity {
166 std::vector<PosixFileIdentity> lock_identities;
167 lock_identities.reserve(lock_paths.size());
168 for (
const fs::path& path : lock_paths) {
169 int open_flags = O_RDWR | O_CREAT;
171 open_flags |= O_CLOEXEC;
174 open_flags |= O_NOFOLLOW;
176 const int fd = open(path.c_str(), open_flags, S_IRUSR | S_IWUSR);
178 return absl::PermissionDeniedError(absl::StrFormat(
179 "Cannot open %s lock %s: %s", labels.
subject, path.string(),
180 std::error_code(errno, std::generic_category()).message()));
182 struct stat lock_stat{};
183 if (fstat(fd, &lock_stat) != 0) {
184 const int error = errno;
186 return absl::FailedPreconditionError(absl::StrFormat(
187 "Cannot inspect %s lock %s: %s", labels.
subject, path.string(),
188 std::error_code(error, std::generic_category()).message()));
190 if (!S_ISREG(lock_stat.st_mode)) {
192 return absl::FailedPreconditionError(
193 absl::StrFormat(
"%s lock must be a regular file: %s",
196 if (lock_stat.st_nlink != 1) {
198 return absl::FailedPreconditionError(
199 absl::StrFormat(
"%s lock must have exactly one hard link: %s",
202 const PosixFileIdentity identity = {lock_stat.st_dev, lock_stat.st_ino};
203 const bool duplicate_identity =
204 std::any_of(lock_identities.begin(), lock_identities.end(),
205 [&](
const PosixFileIdentity& existing) {
206 return existing.device == identity.device &&
207 existing.inode == identity.inode;
209 if (duplicate_identity) {
211 return absl::InvalidArgumentError(
212 absl::StrFormat(
"%s lock aliases another lock path: %s",
215 if (fchmod(fd, S_IRUSR | S_IWUSR) != 0) {
216 const int error = errno;
218 return absl::PermissionDeniedError(absl::StrFormat(
219 "Cannot secure %s lock %s: %s", labels.
subject, path.string(),
220 std::error_code(error, std::generic_category()).message()));
222 while (flock(fd, LOCK_EX) != 0) {
223 if (errno == EINTR) {
226 const int error = errno;
228 return absl::UnavailableError(absl::StrFormat(
229 "Cannot acquire %s lock %s: %s", labels.
subject, path.string(),
230 std::error_code(error, std::generic_category()).message()));
232 lock_identities.push_back(identity);
233 lock->fds_.push_back(fd);