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