/src/ostree/libglnx/glnx-lockfile.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | | |
3 | | /*** |
4 | | This file is part of systemd. |
5 | | Now copied into libglnx: |
6 | | - Use GError |
7 | | |
8 | | Copyright 2010 Lennart Poettering |
9 | | Copyright 2015 Colin Walters <walters@verbum.org> |
10 | | SPDX-License-Identifier: LGPL-2.1-or-later |
11 | | |
12 | | systemd is free software; you can redistribute it and/or modify it |
13 | | under the terms of the GNU Lesser General Public License as published by |
14 | | the Free Software Foundation; either version 2.1 of the License, or |
15 | | (at your option) any later version. |
16 | | |
17 | | systemd is distributed in the hope that it will be useful, but |
18 | | WITHOUT ANY WARRANTY; without even the implied warranty of |
19 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
20 | | Lesser General Public License for more details. |
21 | | |
22 | | You should have received a copy of the GNU Lesser General Public License |
23 | | along with systemd; If not, see <http://www.gnu.org/licenses/>. |
24 | | ***/ |
25 | | |
26 | | #include "libglnx-config.h" |
27 | | |
28 | | #include <stdlib.h> |
29 | | #include <stdbool.h> |
30 | | #include <errno.h> |
31 | | #include <string.h> |
32 | | #include <stdio.h> |
33 | | #include <limits.h> |
34 | | #include <unistd.h> |
35 | | #include <sys/types.h> |
36 | | #include <sys/file.h> |
37 | | #include <sys/stat.h> |
38 | | #include <fcntl.h> |
39 | | |
40 | | #include "glnx-lockfile.h" |
41 | | #include "glnx-errors.h" |
42 | | #include "glnx-fdio.h" |
43 | | #include "glnx-backport-autocleanups.h" |
44 | | #include "glnx-local-alloc.h" |
45 | | |
46 | | #define newa(t, n) ((t*) alloca(sizeof(t)*(n))) |
47 | | |
48 | | /** |
49 | | * glnx_make_lock_file: |
50 | | * @dfd: Directory file descriptor (if not `AT_FDCWD`, must have lifetime `>=` @out_lock) |
51 | | * @p: Path |
52 | | * @operation: one of `LOCK_SH`, `LOCK_EX`, `LOCK_UN`, as passed to flock() |
53 | | * @out_lock: (out) (caller allocates): Return location for lock |
54 | | * @error: Error |
55 | | * |
56 | | * Block until a lock file named @p (relative to @dfd) can be created, |
57 | | * using the flags in @operation, returning the lock data in the |
58 | | * caller-allocated location @out_lock. |
59 | | * |
60 | | * This API wraps new-style process locking if available, otherwise |
61 | | * falls back to BSD locks. |
62 | | */ |
63 | | gboolean |
64 | 0 | glnx_make_lock_file(int dfd, const char *p, int operation, GLnxLockFile *out_lock, GError **error) { |
65 | 0 | glnx_autofd int fd = -1; |
66 | 0 | g_autofree char *t = NULL; |
67 | 0 | int r; |
68 | | |
69 | | /* |
70 | | * We use UNPOSIX locks if they are available. They have nice |
71 | | * semantics, and are mostly compatible with NFS. However, |
72 | | * they are only available on new kernels. When we detect we |
73 | | * are running on an older kernel, then we fall back to good |
74 | | * old BSD locks. They also have nice semantics, but are |
75 | | * slightly problematic on NFS, where they are upgraded to |
76 | | * POSIX locks, even though locally they are orthogonal to |
77 | | * POSIX locks. |
78 | | */ |
79 | |
|
80 | 0 | t = g_strdup(p); |
81 | |
|
82 | 0 | for (;;) { |
83 | 0 | #ifdef F_OFD_SETLK |
84 | 0 | struct flock fl = { |
85 | 0 | .l_type = (operation & ~LOCK_NB) == LOCK_EX ? F_WRLCK : F_RDLCK, |
86 | 0 | .l_whence = SEEK_SET, |
87 | 0 | }; |
88 | 0 | #endif |
89 | 0 | struct stat st; |
90 | |
|
91 | 0 | fd = openat(dfd, p, O_CREAT|O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NOCTTY, 0600); |
92 | 0 | if (fd < 0) |
93 | 0 | return glnx_throw_errno(error); |
94 | | |
95 | | /* Unfortunately, new locks are not in RHEL 7.1 glibc */ |
96 | 0 | #ifdef F_OFD_SETLK |
97 | 0 | r = fcntl(fd, (operation & LOCK_NB) ? F_OFD_SETLK : F_OFD_SETLKW, &fl); |
98 | | #else |
99 | | r = -1; |
100 | | errno = EINVAL; |
101 | | #endif |
102 | 0 | if (r < 0) { |
103 | | |
104 | | /* If the kernel is too old, use good old BSD locks */ |
105 | 0 | if (errno == EINVAL) |
106 | 0 | r = flock(fd, operation); |
107 | |
|
108 | 0 | if (r < 0) |
109 | 0 | return glnx_throw_errno_prefix (error, "flock"); |
110 | 0 | } |
111 | | |
112 | | /* If we acquired the lock, let's check if the file |
113 | | * still exists in the file system. If not, then the |
114 | | * previous exclusive owner removed it and then closed |
115 | | * it. In such a case our acquired lock is worthless, |
116 | | * hence try again. */ |
117 | | |
118 | 0 | if (!glnx_fstat (fd, &st, error)) |
119 | 0 | return FALSE; |
120 | 0 | if (st.st_nlink > 0) |
121 | 0 | break; |
122 | | |
123 | 0 | glnx_close_fd (&fd); |
124 | 0 | } |
125 | | |
126 | | /* Note that if this is not AT_FDCWD, the caller takes responsibility |
127 | | * for the fd's lifetime being >= that of the lock. |
128 | | */ |
129 | 0 | out_lock->initialized = TRUE; |
130 | 0 | out_lock->dfd = dfd; |
131 | 0 | out_lock->path = g_steal_pointer (&t); |
132 | 0 | out_lock->fd = g_steal_fd (&fd); |
133 | 0 | out_lock->operation = operation; |
134 | 0 | return TRUE; |
135 | 0 | } |
136 | | |
137 | 845 | void glnx_release_lock_file(GLnxLockFile *f) { |
138 | 845 | int r; |
139 | | |
140 | 845 | if (!(f && f->initialized)) |
141 | 845 | return; |
142 | | |
143 | 0 | if (f->path) { |
144 | | |
145 | | /* If we are the exclusive owner we can safely delete |
146 | | * the lock file itself. If we are not the exclusive |
147 | | * owner, we can try becoming it. */ |
148 | |
|
149 | 0 | if (f->fd >= 0 && |
150 | 0 | (f->operation & ~LOCK_NB) == LOCK_SH) { |
151 | 0 | #ifdef F_OFD_SETLK |
152 | 0 | static const struct flock fl = { |
153 | 0 | .l_type = F_WRLCK, |
154 | 0 | .l_whence = SEEK_SET, |
155 | 0 | }; |
156 | |
|
157 | 0 | r = fcntl(f->fd, F_OFD_SETLK, &fl); |
158 | | #else |
159 | | r = -1; |
160 | | errno = EINVAL; |
161 | | #endif |
162 | 0 | if (r < 0 && errno == EINVAL) |
163 | 0 | r = flock(f->fd, LOCK_EX|LOCK_NB); |
164 | |
|
165 | 0 | if (r >= 0) |
166 | 0 | f->operation = LOCK_EX|LOCK_NB; |
167 | 0 | } |
168 | |
|
169 | 0 | if ((f->operation & ~LOCK_NB) == LOCK_EX) { |
170 | 0 | (void) unlinkat(f->dfd, f->path, 0); |
171 | 0 | } |
172 | |
|
173 | 0 | g_free(f->path); |
174 | 0 | f->path = NULL; |
175 | 0 | } |
176 | |
|
177 | 0 | glnx_close_fd (&f->fd); |
178 | 0 | f->operation = 0; |
179 | 0 | f->initialized = FALSE; |
180 | 0 | } |