cpp-common/bt2c/fmt.hpp: use `wise_enum::string_type` in `EnableIfIsWiseEnum` definition
[babeltrace.git] / src / fd-cache / fd-cache.c
1 /*
2 * SPDX-License-Identifier: MIT
3 *
4 * Copyright 2019 Francis Deslauriers <francis.deslauriers@efficios.com>
5 *
6 * Babeltrace - File descriptor cache
7 */
8
9 #define BT_LOG_OUTPUT_LEVEL (fdc->log_level)
10 #define BT_LOG_TAG "FD-CACHE"
11 #include "logging/log.h"
12
13 #include <fcntl.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17 #include <glib.h>
18
19 #include "common/assert.h"
20 #include "fd-cache.h"
21
22 struct file_key {
23 uint64_t dev;
24 uint64_t ino;
25 };
26
27 struct fd_handle_internal {
28 struct bt_fd_cache_handle fd_handle;
29 uint64_t ref_count;
30 struct file_key *key;
31 };
32
33 static
34 void fd_cache_handle_internal_destroy(
35 struct fd_handle_internal *internal_fd)
36 {
37 if (!internal_fd) {
38 goto end;
39 }
40
41 if (internal_fd->fd_handle.fd >= 0) {
42 close(internal_fd->fd_handle.fd);
43 internal_fd->fd_handle.fd = -1;
44 }
45
46 end:
47 g_free(internal_fd);
48 }
49
50 /*
51 * Using simple hash algorithm found on stackoverflow:
52 * https://stackoverflow.com/questions/664014/
53 */
54 static inline
55 uint64_t hash_uint64_t(uint64_t x) {
56 x = (x ^ (x >> 30)) * UINT64_C(0xbf58476d1ce4e5b9);
57 x = (x ^ (x >> 27)) * UINT64_C(0x94d049bb133111eb);
58 x = x ^ (x >> 31);
59 return x;
60 }
61
62 static
63 guint file_key_hash(gconstpointer v)
64 {
65 const struct file_key *fk = v;
66 return hash_uint64_t(fk->dev) ^ hash_uint64_t(fk->ino);
67 }
68
69 static
70 gboolean file_key_equal(gconstpointer v1, gconstpointer v2)
71 {
72 const struct file_key *fk1 = v1;
73 const struct file_key *fk2 = v2;
74
75 return (fk1->dev == fk2->dev) && (fk1->ino == fk2->ino);
76 }
77
78 static
79 void file_key_destroy(gpointer data)
80 {
81 struct file_key *fk = data;
82 g_free(fk);
83 }
84
85 int bt_fd_cache_init(struct bt_fd_cache *fdc, int log_level)
86 {
87 int ret = 0;
88
89 fdc->log_level = log_level;
90 fdc->cache = g_hash_table_new_full(file_key_hash, file_key_equal,
91 file_key_destroy, (GDestroyNotify) fd_cache_handle_internal_destroy);
92 if (!fdc->cache) {
93 ret = -1;
94 }
95
96 return ret;
97 }
98
99 void bt_fd_cache_fini(struct bt_fd_cache *fdc)
100 {
101 if (!fdc->cache) {
102 goto end;
103 }
104
105 /*
106 * All handle should have been removed for the hashtable at this point.
107 */
108 BT_ASSERT(g_hash_table_size(fdc->cache) == 0);
109 g_hash_table_destroy(fdc->cache);
110
111 end:
112 return;
113 }
114
115 struct bt_fd_cache_handle *bt_fd_cache_get_handle(struct bt_fd_cache *fdc,
116 const char *path)
117 {
118 struct fd_handle_internal *fd_internal = NULL;
119 struct stat statbuf;
120 struct file_key fk;
121 int ret, fd = -1;
122
123 ret = stat(path, &statbuf);
124 if (ret < 0) {
125 /*
126 * This is not necessarily an error as we sometimes try to open
127 * files to see if they exist. Log the error as DEBUG severity
128 * level.
129 */
130 BT_LOGD_ERRNO("Failed to stat file", ": path=%s", path);
131 goto end;
132 }
133
134 /*
135 * Use the device number and inode number to uniquely identify a file.
136 * Even if the file has the same path, it may have been replaced so we
137 * must open a new FD for it. This replacement of file is more likely
138 * to happen with a lttng-live source component.
139 */
140 fk.dev = statbuf.st_dev;
141 fk.ino = statbuf.st_ino;
142
143 fd_internal = g_hash_table_lookup(fdc->cache, &fk);
144 if (!fd_internal) {
145 struct file_key *file_key;
146
147 fd = open(path, O_RDONLY);
148 if (fd < 0) {
149 BT_LOGE_ERRNO("Failed to open file", "path=%s", path);
150 goto error;
151 }
152
153 fd_internal = g_new0(struct fd_handle_internal, 1);
154 if (!fd_internal) {
155 BT_LOGE_STR("Failed to allocate internal FD handle.");
156 goto error;
157 }
158
159 file_key = g_new0(struct file_key, 1);
160 if (!fd_internal) {
161 BT_LOGE_STR("Failed to allocate file key.");
162 goto error;
163 }
164
165 *file_key = fk;
166
167 fd_internal->fd_handle.fd = fd;
168 fd_internal->ref_count = 0;
169 fd_internal->key = file_key;
170
171 /* Insert the newly created fd handle. */
172 g_hash_table_insert(fdc->cache, fd_internal->key, fd_internal);
173 }
174
175 fd_internal->ref_count++;
176 goto end;
177
178 error:
179 /*
180 * Close file descriptor if it was open() and we are currently on error
181 * path.
182 */
183 if (fd != -1) {
184 ret = close(fd);
185 if (ret) {
186 BT_LOGE_ERRNO("Failed to close file descriptor",
187 ": fd=%i, path=%s", fd, path);
188 }
189 }
190
191 fd_cache_handle_internal_destroy(fd_internal);
192 fd_internal = NULL;
193 end:
194 return (struct bt_fd_cache_handle *) fd_internal;
195 }
196
197 void bt_fd_cache_put_handle(struct bt_fd_cache *fdc,
198 struct bt_fd_cache_handle *handle)
199 {
200 struct fd_handle_internal *fd_internal;
201
202 if (!handle) {
203 goto end;
204 }
205
206 fd_internal = (struct fd_handle_internal *) handle;
207
208 BT_ASSERT(fd_internal->ref_count > 0);
209
210 if (fd_internal->ref_count > 1) {
211 fd_internal->ref_count--;
212 } else {
213 gboolean ret;
214 int close_ret;
215
216 close_ret = close(fd_internal->fd_handle.fd);
217 if (close_ret == -1) {
218 BT_LOGE_ERRNO("Failed to close file descriptor",
219 ": fd=%d", fd_internal->fd_handle.fd);
220 }
221 ret = g_hash_table_remove(fdc->cache, fd_internal->key);
222 BT_ASSERT(ret);
223 }
224
225 end:
226 return;
227 }
This page took 0.033701 seconds and 5 git commands to generate.