fd-tracker: add the lttng-inode interface
[lttng-tools.git] / src / common / fd-tracker / inode.c
CommitLineData
e0e72660
JG
1/*
2 * Copyright (C) 2018 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License, version 2 only, as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 *
13 * You should have received a copy of the GNU General Public License along with
14 * this program; if not, write to the Free Software Foundation, Inc., 51
15 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16 */
17
18#include <sys/types.h>
19#include <sys/stat.h>
20#include <unistd.h>
21#include <inttypes.h>
22#include <urcu.h>
23#include <urcu/ref.h>
24#include <urcu/rculfhash.h>
25#include <common/hashtable/utils.h>
26#include <common/macros.h>
27#include <common/defaults.h>
28#include <common/error.h>
29#include <lttng/constant.h>
30
31#include "inode.h"
32
33struct inode_id {
34 dev_t device;
35 ino_t inode;
36};
37
38struct lttng_inode_registry {
39 /* Hashtable of inode_id to lttng_inode. */
40 struct cds_lfht *inodes;
41};
42
43struct lttng_inode {
44 struct inode_id id;
45 char *path;
46 bool unlink_pending;
47 /* Node in the lttng_inode_registry's ht. */
48 struct cds_lfht_node registry_node;
49 /* Weak reference to ht containing the node. */
50 struct cds_lfht *registry_ht;
51 struct urcu_ref ref;
52 struct rcu_head rcu_head;
53};
54
55static struct {
56 pthread_mutex_t lock;
57 bool initialized;
58 unsigned long value;
59} seed = {
60 .lock = PTHREAD_MUTEX_INITIALIZER,
61};
62
63static
64unsigned long lttng_inode_id_hash(struct inode_id *id)
65{
66 uint64_t device = id->device, inode_no = id->inode;
67
68 return hash_key_u64(&device, seed.value) ^
69 hash_key_u64(&inode_no, seed.value);
70}
71
72static
73int lttng_inode_match(struct cds_lfht_node *node, const void *key)
74{
75 const struct inode_id *id = key;
76 struct lttng_inode *inode = caa_container_of(node, struct lttng_inode,
77 registry_node);
78
79 return inode->id.device == id->device && inode->id.inode == id->inode;
80}
81
82static
83void lttng_inode_delete(struct rcu_head *head)
84{
85 struct lttng_inode *inode = caa_container_of(head,
86 struct lttng_inode, rcu_head);
87
88 free(inode->path);
89 free(inode);
90}
91
92static
93void lttng_inode_destroy(struct lttng_inode *inode)
94{
95 if (!inode) {
96 return;
97 }
98 if (inode->unlink_pending) {
99 int ret = unlink(inode->path);
100
101 DBG("Unlinking %s during lttng_inode destruction", inode->path);
102 if (ret) {
103 PERROR("Failed to unlink %s", inode->path);
104 }
105 }
106 rcu_read_lock();
107 cds_lfht_del(inode->registry_ht, &inode->registry_node);
108 rcu_read_unlock();
109 call_rcu(&inode->rcu_head, lttng_inode_delete);
110}
111
112static
113void lttng_inode_release(struct urcu_ref *ref)
114{
115 lttng_inode_destroy(caa_container_of(ref, struct lttng_inode, ref));
116}
117
118static
119void lttng_inode_get(struct lttng_inode *inode)
120{
121 urcu_ref_get(&inode->ref);
122}
123
124void lttng_inode_put(struct lttng_inode *inode)
125{
126 urcu_ref_put(&inode->ref, lttng_inode_release);
127}
128
129const char *lttng_inode_get_path(const struct lttng_inode *inode)
130{
131 return inode->path;
132}
133
134int lttng_inode_rename(struct lttng_inode *inode, const char *new_path,
135 bool overwrite)
136{
137 int ret = 0;
138 char *new_path_copy = NULL;
139
140 if (inode->unlink_pending) {
141 WARN("An attempt to rename an unlinked file, %s to %s, has been performed",
142 inode->path, new_path);
143 ret = -ENOENT;
144 goto end;
145 }
146
147 if (!overwrite) {
148 struct stat statbuf;
149
150 ret = stat(new_path, &statbuf);
151 if (ret == 0) {
152 ret = -EEXIST;
153 goto end;
154 } else if (ret < 0 && errno != ENOENT) {
155 PERROR("Failed to stat() %s", new_path);
156 ret = -errno;
157 goto end;
158 }
159 }
160
161 new_path_copy = strdup(new_path);
162 if (!new_path_copy) {
163 ERR("Failed to allocate storage for path %s", new_path);
164 ret = -ENOMEM;
165 goto end;
166 }
167
168 ret = rename(inode->path, new_path);
169 if (ret) {
170 PERROR("Failed to rename %s to %s", inode->path, new_path);
171 ret = -errno;
172 goto end;
173 }
174
175 free(inode->path);
176 inode->path = new_path_copy;
177 new_path_copy = NULL;
178end:
179 free(new_path_copy);
180 return ret;
181}
182
183int lttng_inode_defer_unlink(struct lttng_inode *inode)
184{
185 int ret = 0;
186 uint16_t i = 0;
187 char suffix[sizeof("-deleted-65535")] = "-deleted";
188 char new_path[LTTNG_PATH_MAX];
189 size_t original_path_len = strlen(inode->path);
190
191 if (inode->unlink_pending) {
192 WARN("An attempt to re-unlink %s has been performed, ignoring.",
193 inode->path);
194 ret = -ENOENT;
195 goto end;
196 }
197
198 ret = lttng_strncpy(new_path, inode->path, sizeof(new_path));
199 if (ret < 0) {
200 ret = -ENAMETOOLONG;
201 goto end;
202 }
203
204 for (i = 0; i < UINT16_MAX; i++) {
205 int p_ret;
206
207 if (i != 0) {
208 p_ret = snprintf(suffix, sizeof(suffix), "-deleted-%" PRIu16, i);
209
210 if (p_ret < 0) {
211 PERROR("Failed to form suffix to rename file %s",
212 inode->path);
213 ret = -errno;
214 goto end;
215 }
216 assert(p_ret != sizeof(suffix));
217 } else {
218 /* suffix is initialy set to '-deleted'. */
219 p_ret = strlen(suffix);
220 }
221
222 if (original_path_len + p_ret + 1 >= sizeof(new_path)) {
223 ret = -ENAMETOOLONG;
224 goto end;
225 }
226
227 strcat(&new_path[original_path_len], suffix);
228 ret = lttng_inode_rename(inode, new_path, false);
229 if (ret != -EEXIST) {
230 break;
231 }
232 new_path[original_path_len] = '\0';
233 }
234 if (!ret) {
235 inode->unlink_pending = true;
236 }
237end:
238 return ret;
239}
240
241static
242struct lttng_inode *lttng_inode_create(const struct inode_id *id,
243 const char *path, struct cds_lfht *ht)
244{
245 struct lttng_inode *inode = zmalloc(sizeof(*inode));
246
247 if (!inode) {
248 goto end;
249 }
250
251 urcu_ref_init(&inode->ref);
252 cds_lfht_node_init(&inode->registry_node);
253 inode->id = *id;
254 inode->path = strdup(path);
255 inode->registry_ht = ht;
256 if (!inode->path) {
257 goto error;
258 }
259end:
260 return inode;
261error:
262 lttng_inode_destroy(inode);
263 return NULL;
264}
265
266struct lttng_inode_registry *lttng_inode_registry_create(void)
267{
268 struct lttng_inode_registry *registry = zmalloc(sizeof(*registry));
269
270 if (!registry) {
271 goto end;
272 }
273
274 pthread_mutex_lock(&seed.lock);
275 if (!seed.initialized) {
276 seed.value = (unsigned long) time(NULL);
277 seed.initialized = true;
278 }
279 pthread_mutex_unlock(&seed.lock);
280
281 registry->inodes = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
282 CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
283 if (!registry->inodes) {
284 goto error;
285 }
286end:
287 return registry;
288error:
289 lttng_inode_registry_destroy(registry);
290 return NULL;
291}
292
293void lttng_inode_registry_destroy(struct lttng_inode_registry *registry)
294{
295 if (!registry) {
296 return;
297 }
298 if (registry->inodes) {
299 int ret = cds_lfht_destroy(registry->inodes, NULL);
300
301 assert(!ret);
302 }
303 free(registry);
304}
305
306struct lttng_inode *lttng_inode_registry_get_inode(
307 struct lttng_inode_registry *registry,
308 int fd, const char *path)
309{
310 int ret;
311 struct stat statbuf;
312 struct inode_id id;
313 struct cds_lfht_iter iter;
314 struct cds_lfht_node *node;
315 struct lttng_inode *inode = NULL;
316
317 ret = fstat(fd, &statbuf);
318 if (ret < 0) {
319 PERROR("stat() failed on file %s, fd = %i", path, fd);
320 goto end;
321 }
322
323 id.device = statbuf.st_dev;
324 id.inode = statbuf.st_ino;
325
326 rcu_read_lock();
327 cds_lfht_lookup(registry->inodes,
328 lttng_inode_id_hash(&id),
329 lttng_inode_match,
330 &id,
331 &iter);
332 node = cds_lfht_iter_get_node(&iter);
333 if (node) {
334 inode = caa_container_of(node, struct lttng_inode, registry_node);
335 /* Renames should happen through the fs-handle interface. */
336 assert(!strcmp(path, inode->path));
337 lttng_inode_get(inode);
338 goto end_unlock;
339 }
340
341 inode = lttng_inode_create(&id, path, registry->inodes);
342 node = cds_lfht_add_unique(registry->inodes,
343 lttng_inode_id_hash(&inode->id),
344 lttng_inode_match,
345 &inode->id,
346 &inode->registry_node);
347 assert(node == &inode->registry_node);
348end_unlock:
349 rcu_read_unlock();
350end:
351 return inode;
352}
This page took 0.035135 seconds and 4 git commands to generate.