c2907a04cb3880029f45bbfd4fbe9337f5757371
[babeltrace.git] / src / fd-cache / fd-cache.c
1 /*
2 * fd-cache.c
3 *
4 * Babeltrace - File descriptor cache
5 *
6 * Copyright 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
7 *
8 * Author: Francis Deslauriers <francis.deslauriers@efficios.com>
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in
18 * all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 */
28
29 #define BT_LOG_OUTPUT_LEVEL (fdc->log_level)
30 #define BT_LOG_TAG "FD-CACHE"
31 #include "logging/log.h"
32
33 #include <fcntl.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <unistd.h>
37 #include <glib.h>
38
39 #include "common/assert.h"
40 #include "fd-cache.h"
41
42 struct file_key {
43 uint64_t dev;
44 uint64_t ino;
45 };
46
47 struct fd_handle_internal {
48 struct bt_fd_cache_handle fd_handle;
49 uint64_t ref_count;
50 struct file_key *key;
51 };
52
53 static
54 void fd_cache_handle_internal_destroy(
55 struct fd_handle_internal *internal_fd)
56 {
57 if (!internal_fd) {
58 goto end;
59 }
60
61 if (internal_fd->fd_handle.fd >= 0) {
62 close(internal_fd->fd_handle.fd);
63 internal_fd->fd_handle.fd = -1;
64 }
65
66 end:
67 g_free(internal_fd);
68 }
69
70 /*
71 * Using simple hash algorithm found on stackoverflow:
72 * https://stackoverflow.com/questions/664014/
73 */
74 static inline
75 uint64_t hash_uint64_t(uint64_t x) {
76 x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
77 x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
78 x = x ^ (x >> 31);
79 return x;
80 }
81
82 static
83 guint file_key_hash(gconstpointer v)
84 {
85 const struct file_key *fk = v;
86 return hash_uint64_t(fk->dev) ^ hash_uint64_t(fk->ino);
87 }
88
89 static
90 gboolean file_key_equal(gconstpointer v1, gconstpointer v2)
91 {
92 const struct file_key *fk1 = v1;
93 const struct file_key *fk2 = v2;
94
95 return (fk1->dev == fk2->dev) && (fk1->ino == fk2->ino);
96 }
97
98 static
99 void file_key_destroy(gpointer data)
100 {
101 struct file_key *fk = data;
102 g_free(fk);
103 }
104
105 BT_HIDDEN
106 int bt_fd_cache_init(struct bt_fd_cache *fdc, int log_level)
107 {
108 int ret = 0;
109
110 fdc->log_level = log_level;
111 fdc->cache = g_hash_table_new_full(file_key_hash, file_key_equal,
112 file_key_destroy, (GDestroyNotify) fd_cache_handle_internal_destroy);
113 if (!fdc->cache) {
114 ret = -1;
115 }
116
117 return ret;
118 }
119
120 BT_HIDDEN
121 void bt_fd_cache_fini(struct bt_fd_cache *fdc)
122 {
123 if (!fdc->cache) {
124 goto end;
125 }
126
127 /*
128 * All handle should have been removed for the hashtable at this point.
129 */
130 BT_ASSERT(g_hash_table_size(fdc->cache) == 0);
131 g_hash_table_destroy(fdc->cache);
132
133 end:
134 return;
135 }
136
137 BT_HIDDEN
138 struct bt_fd_cache_handle *bt_fd_cache_get_handle(struct bt_fd_cache *fdc,
139 const char *path)
140 {
141 struct fd_handle_internal *fd_internal = NULL;
142 struct stat statbuf;
143 struct file_key fk;
144 int ret, fd = -1;
145
146 ret = stat(path, &statbuf);
147 if (ret < 0) {
148 /*
149 * This is not necessarily an error as we sometimes try to open
150 * files to see if they exist. Log the error as DEBUG severity
151 * level.
152 */
153 BT_LOGD_ERRNO("Failed to stat file", ": path=%s", path);
154 goto end;
155 }
156
157 /*
158 * Use the device number and inode number to uniquely identify a file.
159 * Even if the file has the same path, it may have been replaced so we
160 * must open a new FD for it. This replacement of file is more likely
161 * to happen with a lttng-live source component.
162 */
163 fk.dev = statbuf.st_dev;
164 fk.ino = statbuf.st_ino;
165
166 fd_internal = g_hash_table_lookup(fdc->cache, &fk);
167 if (!fd_internal) {
168 struct file_key *file_key;
169
170 fd = open(path, O_RDONLY);
171 if (fd < 0) {
172 BT_LOGE_ERRNO("Failed to open file", "path=%s", path);
173 goto error;
174 }
175
176 fd_internal = g_new0(struct fd_handle_internal, 1);
177 if (!fd_internal) {
178 BT_LOGE_STR("Failed to allocate internal FD handle.");
179 goto error;
180 }
181
182 file_key = g_new0(struct file_key, 1);
183 if (!fd_internal) {
184 BT_LOGE_STR("Failed to allocate file key.");
185 goto error;
186 }
187
188 *file_key = fk;
189
190 fd_internal->fd_handle.fd = fd;
191 fd_internal->ref_count = 0;
192 fd_internal->key = file_key;
193
194 /* Insert the newly created fd handle. */
195 g_hash_table_insert(fdc->cache, fd_internal->key, fd_internal);
196 }
197
198 fd_internal->ref_count++;
199 goto end;
200
201 error:
202 /*
203 * Close file descriptor if it was open() and we are currently on error
204 * path.
205 */
206 if (fd != -1) {
207 ret = close(fd);
208 if (ret) {
209 BT_LOGE_ERRNO("Failed to close file descriptor",
210 ": fd=%i, path=%s", fd, path);
211 }
212 }
213
214 fd_cache_handle_internal_destroy(fd_internal);
215 fd_internal = NULL;
216 end:
217 return (struct bt_fd_cache_handle *) fd_internal;
218 }
219
220 BT_HIDDEN
221 void bt_fd_cache_put_handle(struct bt_fd_cache *fdc,
222 struct bt_fd_cache_handle *handle)
223 {
224 struct fd_handle_internal *fd_internal;
225
226 if (!handle) {
227 goto end;
228 }
229
230 fd_internal = (struct fd_handle_internal *) handle;
231
232 BT_ASSERT(fd_internal->ref_count > 0);
233
234 if (fd_internal->ref_count > 1) {
235 fd_internal->ref_count--;
236 } else {
237 gboolean ret;
238 int close_ret;
239
240 close_ret = close(fd_internal->fd_handle.fd);
241 if (close_ret == -1) {
242 BT_LOGE_ERRNO("Failed to close file descriptor",
243 ": fd=%d", fd_internal->fd_handle.fd);
244 }
245 ret = g_hash_table_remove(fdc->cache, fd_internal->key);
246 BT_ASSERT(ret);
247 }
248
249 end:
250 return;
251 }
This page took 0.034432 seconds and 4 git commands to generate.