Build fix: missing stdio.h include in signal-helper.hpp
[lttng-tools.git] / src / common / fd-tracker / inode.cpp
CommitLineData
e0e72660 1/*
ab5be9fa 2 * Copyright (C) 2020 Jérémie Galarneau <jeremie.galarneau@efficios.com>
e0e72660 3 *
ab5be9fa 4 * SPDX-License-Identifier: GPL-2.0-only
e0e72660 5 *
e0e72660
JG
6 */
7
c9e313bc
SM
8#include <common/defaults.hpp>
9#include <common/error.hpp>
10#include <common/hashtable/utils.hpp>
11#include <common/macros.hpp>
12#include <common/optional.hpp>
13#include <common/string-utils/format.hpp>
14#include <common/utils.hpp>
5c1f54d1
JG
15#include <inttypes.h>
16#include <lttng/constant.h>
e0e72660 17#include <sys/stat.h>
5c1f54d1 18#include <sys/types.h>
e0e72660 19#include <unistd.h>
e0e72660 20#include <urcu.h>
e0e72660 21#include <urcu/rculfhash.h>
5c1f54d1 22#include <urcu/ref.h>
e0e72660 23
c9e313bc 24#include "inode.hpp"
e0e72660 25
f1494934 26namespace {
e0e72660
JG
27struct inode_id {
28 dev_t device;
29 ino_t inode;
30};
f1494934 31} /* namespace */
e0e72660
JG
32
33struct lttng_inode_registry {
34 /* Hashtable of inode_id to lttng_inode. */
35 struct cds_lfht *inodes;
36};
37
38struct lttng_inode {
39 struct inode_id id;
e0e72660
JG
40 /* Node in the lttng_inode_registry's ht. */
41 struct cds_lfht_node registry_node;
42 /* Weak reference to ht containing the node. */
43 struct cds_lfht *registry_ht;
44 struct urcu_ref ref;
45 struct rcu_head rcu_head;
f7c3ffd7
JG
46 /* Location from which this file can be opened. */
47 struct {
48 struct lttng_directory_handle *directory_handle;
49 char *path;
50 } location;
51 /* Unlink the underlying file at the release of the inode. */
52 bool unlink_pending;
53 LTTNG_OPTIONAL(unsigned int) unlinked_id;
54 /* Weak reference. */
55 struct lttng_unlinked_file_pool *unlinked_file_pool;
56};
57
58struct lttng_unlinked_file_pool {
59 struct lttng_directory_handle *unlink_directory_handle;
60 char *unlink_directory_path;
61 unsigned int file_count;
62 unsigned int next_id;
e0e72660
JG
63};
64
f1494934
JG
65namespace {
66struct {
e0e72660
JG
67 pthread_mutex_t lock;
68 bool initialized;
69 unsigned long value;
70} seed = {
5c1f54d1 71 .lock = PTHREAD_MUTEX_INITIALIZER,
1c9a0b0e
MJ
72 .initialized = false,
73 .value = 0,
e0e72660 74};
f1494934 75} /* namespace */
e0e72660 76
f7c3ffd7 77static unsigned long lttng_inode_id_hash(const struct inode_id *id)
e0e72660
JG
78{
79 uint64_t device = id->device, inode_no = id->inode;
80
5c1f54d1
JG
81 return hash_key_u64(&device, seed.value) ^
82 hash_key_u64(&inode_no, seed.value);
e0e72660
JG
83}
84
5c1f54d1 85static int lttng_inode_match(struct cds_lfht_node *node, const void *key)
e0e72660 86{
e032c6fd 87 const struct inode_id *id = (inode_id *) key;
0114db0e
JG
88 const struct lttng_inode *inode = lttng::utils::container_of(
89 node, &lttng_inode::registry_node);
e0e72660
JG
90
91 return inode->id.device == id->device && inode->id.inode == id->inode;
92}
93
f7c3ffd7 94static void lttng_inode_free(struct rcu_head *head)
e0e72660 95{
5c1f54d1 96 struct lttng_inode *inode =
0114db0e 97 lttng::utils::container_of(head, &lttng_inode::rcu_head);
e0e72660 98
e0e72660
JG
99 free(inode);
100}
101
f7c3ffd7
JG
102static int lttng_unlinked_file_pool_add_inode(
103 struct lttng_unlinked_file_pool *pool,
104 struct lttng_inode *inode)
105{
106 int ret;
107 const unsigned int unlinked_id = pool->next_id++;
108 char *inode_unlinked_name;
109 bool reference_acquired;
110
111 DBG("Adding inode of %s to unlinked file pool as id %u",
112 inode->location.path, unlinked_id);
113 ret = asprintf(&inode_unlinked_name, "%u", unlinked_id);
114 if (ret < 0) {
115 ERR("Failed to format unlinked inode name");
116 ret = -1;
117 goto end;
118 }
119
120 if (pool->file_count == 0) {
121 DBG("Creating unlinked files directory at %s",
122 pool->unlink_directory_path);
a0377dfe 123 LTTNG_ASSERT(!pool->unlink_directory_handle);
f7c3ffd7
JG
124 ret = utils_mkdir(pool->unlink_directory_path,
125 S_IRWXU | S_IRWXG, -1, -1);
126 if (ret) {
127 if (errno == EEXIST) {
128 /*
129 * Unexpected (previous crash?), but not an
130 * error.
131 */
132 DBG("Unlinked file directory \"%s\" already exists",
133 pool->unlink_directory_path);
134 } else {
135 PERROR("Failed to create unlinked files directory at %s",
136 pool->unlink_directory_path);
137 goto end;
138 }
139 }
140 pool->unlink_directory_handle = lttng_directory_handle_create(
141 pool->unlink_directory_path);
d05a3d93
JG
142 if (!pool->unlink_directory_handle) {
143 ERR("Failed to create directory handle to unlinked file pool at %s",
144 pool->unlink_directory_path);
145 ret = -1;
146 goto end;
147 }
f7c3ffd7
JG
148 }
149
150 ret = lttng_directory_handle_rename(inode->location.directory_handle,
151 inode->location.path, pool->unlink_directory_handle,
152 inode_unlinked_name);
153 if (ret) {
154 goto end;
155 }
156
157 lttng_directory_handle_put(inode->location.directory_handle);
158 inode->location.directory_handle = NULL;
159 reference_acquired = lttng_directory_handle_get(
160 pool->unlink_directory_handle);
a0377dfe 161 LTTNG_ASSERT(reference_acquired);
f7c3ffd7
JG
162 inode->location.directory_handle = pool->unlink_directory_handle;
163
164 free(inode->location.path);
165 inode->location.path = inode_unlinked_name;
166 inode_unlinked_name = NULL;
167 LTTNG_OPTIONAL_SET(&inode->unlinked_id, unlinked_id);
168 pool->file_count++;
169end:
170 free(inode_unlinked_name);
171 return ret;
172}
173
174static int lttng_unlinked_file_pool_remove_inode(
175 struct lttng_unlinked_file_pool *pool,
176 struct lttng_inode *inode)
177{
178 int ret;
179
180 DBG("Removing inode with unlinked id %u from unlinked file pool",
181 LTTNG_OPTIONAL_GET(inode->unlinked_id));
182
183 ret = lttng_directory_handle_unlink_file(
184 inode->location.directory_handle, inode->location.path);
185 if (ret) {
186 PERROR("Failed to unlink file %s from unlinked file directory",
187 inode->location.path);
188 goto end;
189 }
190 free(inode->location.path);
191 inode->location.path = NULL;
192 lttng_directory_handle_put(inode->location.directory_handle);
193 inode->location.directory_handle = NULL;
194
195 pool->file_count--;
196 if (pool->file_count == 0) {
197 ret = utils_recursive_rmdir(pool->unlink_directory_path);
198 if (ret) {
199 /*
200 * There is nothing the caller can do, don't report an
201 * error except through logging.
202 */
203 PERROR("Failed to remove unlinked files directory at %s",
204 pool->unlink_directory_path);
205 }
206 lttng_directory_handle_put(pool->unlink_directory_handle);
207 pool->unlink_directory_handle = NULL;
208 }
209end:
210 return ret;
211}
212
5c1f54d1 213static void lttng_inode_destroy(struct lttng_inode *inode)
e0e72660
JG
214{
215 if (!inode) {
216 return;
217 }
f7c3ffd7
JG
218
219 rcu_read_lock();
220 cds_lfht_del(inode->registry_ht, &inode->registry_node);
221 rcu_read_unlock();
222
e0e72660 223 if (inode->unlink_pending) {
f7c3ffd7 224 int ret;
e0e72660 225
a0377dfe
FD
226 LTTNG_ASSERT(inode->location.directory_handle);
227 LTTNG_ASSERT(inode->location.path);
f7c3ffd7
JG
228 DBG("Removing %s from unlinked file pool",
229 inode->location.path);
230 ret = lttng_unlinked_file_pool_remove_inode(inode->unlinked_file_pool, inode);
e0e72660 231 if (ret) {
f7c3ffd7 232 PERROR("Failed to unlink %s", inode->location.path);
e0e72660
JG
233 }
234 }
f7c3ffd7
JG
235
236 lttng_directory_handle_put(
237 inode->location.directory_handle);
238 inode->location.directory_handle = NULL;
239 free(inode->location.path);
240 inode->location.path = NULL;
241 call_rcu(&inode->rcu_head, lttng_inode_free);
e0e72660
JG
242}
243
5c1f54d1 244static void lttng_inode_release(struct urcu_ref *ref)
e0e72660 245{
0114db0e 246 lttng_inode_destroy(lttng::utils::container_of(ref, &lttng_inode::ref));
e0e72660
JG
247}
248
5c1f54d1 249static void lttng_inode_get(struct lttng_inode *inode)
e0e72660
JG
250{
251 urcu_ref_get(&inode->ref);
252}
253
ca806b0b 254struct lttng_unlinked_file_pool *lttng_unlinked_file_pool_create(
f7c3ffd7
JG
255 const char *path)
256{
64803277 257 struct lttng_unlinked_file_pool *pool = zmalloc<lttng_unlinked_file_pool>();
f7c3ffd7 258
9604bd17
JG
259 if (!pool) {
260 goto error;
261 }
262
f7c3ffd7
JG
263 if (!path || *path != '/') {
264 ERR("Unlinked file pool must be created with an absolute path, path = \"%s\"",
265 path ? path : "NULL");
266 goto error;
267 }
268
269 pool->unlink_directory_path = strdup(path);
270 if (!pool->unlink_directory_path) {
271 PERROR("Failed to allocation unlinked file pool path");
272 goto error;
273 }
274 DBG("Unlinked file pool created at: %s", path);
275 return pool;
276error:
277 lttng_unlinked_file_pool_destroy(pool);
278 return NULL;
279}
280
ca806b0b 281void lttng_unlinked_file_pool_destroy(
f7c3ffd7
JG
282 struct lttng_unlinked_file_pool *pool)
283{
284 if (!pool) {
285 return;
286 }
287
a0377dfe 288 LTTNG_ASSERT(pool->file_count == 0);
f7c3ffd7
JG
289 lttng_directory_handle_put(pool->unlink_directory_handle);
290 free(pool->unlink_directory_path);
291 free(pool);
292}
293
ca806b0b 294void lttng_inode_put(struct lttng_inode *inode)
e0e72660
JG
295{
296 urcu_ref_put(&inode->ref, lttng_inode_release);
297}
298
ca806b0b 299struct lttng_directory_handle *
938bd422 300lttng_inode_get_location_directory_handle(
dd95933f
JG
301 struct lttng_inode *inode)
302{
303 if (inode->location.directory_handle) {
304 const bool reference_acquired = lttng_directory_handle_get(
305 inode->location.directory_handle);
306
a0377dfe 307 LTTNG_ASSERT(reference_acquired);
dd95933f
JG
308 }
309 return inode->location.directory_handle;
310}
311
ca806b0b 312void lttng_inode_borrow_location(struct lttng_inode *inode,
f7c3ffd7
JG
313 const struct lttng_directory_handle **out_directory_handle,
314 const char **out_path)
e0e72660 315{
f7c3ffd7
JG
316 if (out_directory_handle) {
317 *out_directory_handle = inode->location.directory_handle;
318 }
319 if (out_path) {
320 *out_path = inode->location.path;
321 }
e0e72660
JG
322}
323
ca806b0b 324int lttng_inode_rename(
f7c3ffd7
JG
325 struct lttng_inode *inode,
326 struct lttng_directory_handle *old_directory_handle,
327 const char *old_path,
328 struct lttng_directory_handle *new_directory_handle,
329 const char *new_path,
330 bool overwrite)
e0e72660
JG
331{
332 int ret = 0;
f7c3ffd7
JG
333 char *new_path_copy = strdup(new_path);
334 bool reference_acquired;
335
336 DBG("Performing rename of inode from %s to %s with %s directory handles",
337 old_path, new_path,
338 lttng_directory_handle_equals(old_directory_handle,
339 new_directory_handle) ?
340 "identical" :
341 "different");
342
343 if (!new_path_copy) {
344 ret = -ENOMEM;
345 goto end;
346 }
e0e72660
JG
347
348 if (inode->unlink_pending) {
f7c3ffd7
JG
349 WARN("An attempt to rename an unlinked file from %s to %s has been performed",
350 old_path, new_path);
e0e72660
JG
351 ret = -ENOENT;
352 goto end;
353 }
354
355 if (!overwrite) {
f7c3ffd7 356 /* Verify that file doesn't exist. */
e0e72660
JG
357 struct stat statbuf;
358
f7c3ffd7
JG
359 ret = lttng_directory_handle_stat(
360 new_directory_handle, new_path, &statbuf);
e0e72660 361 if (ret == 0) {
f7c3ffd7
JG
362 ERR("Refusing to rename %s as the destination already exists",
363 old_path);
e0e72660
JG
364 ret = -EEXIST;
365 goto end;
366 } else if (ret < 0 && errno != ENOENT) {
367 PERROR("Failed to stat() %s", new_path);
368 ret = -errno;
369 goto end;
370 }
371 }
372
f7c3ffd7
JG
373 ret = lttng_directory_handle_rename(old_directory_handle, old_path,
374 new_directory_handle, new_path);
e0e72660 375 if (ret) {
f7c3ffd7 376 PERROR("Failed to rename file %s to %s", old_path, new_path);
e0e72660
JG
377 ret = -errno;
378 goto end;
379 }
380
f7c3ffd7 381 reference_acquired = lttng_directory_handle_get(new_directory_handle);
a0377dfe 382 LTTNG_ASSERT(reference_acquired);
f7c3ffd7
JG
383 lttng_directory_handle_put(inode->location.directory_handle);
384 free(inode->location.path);
385 inode->location.directory_handle = new_directory_handle;
386 /* Ownership transferred. */
387 inode->location.path = new_path_copy;
e0e72660
JG
388 new_path_copy = NULL;
389end:
390 free(new_path_copy);
391 return ret;
392}
393
ca806b0b 394int lttng_inode_unlink(struct lttng_inode *inode)
e0e72660
JG
395{
396 int ret = 0;
f7c3ffd7
JG
397
398 DBG("Attempting unlink of inode %s", inode->location.path);
e0e72660
JG
399
400 if (inode->unlink_pending) {
401 WARN("An attempt to re-unlink %s has been performed, ignoring.",
f7c3ffd7 402 inode->location.path);
e0e72660
JG
403 ret = -ENOENT;
404 goto end;
405 }
406
f7c3ffd7
JG
407 /*
408 * Move to the temporary "deleted" directory until all
409 * references are released.
410 */
411 ret = lttng_unlinked_file_pool_add_inode(
412 inode->unlinked_file_pool, inode);
413 if (ret) {
414 PERROR("Failed to add inode \"%s\" to the unlinked file pool",
415 inode->location.path);
e0e72660
JG
416 goto end;
417 }
f7c3ffd7 418 inode->unlink_pending = true;
e0e72660
JG
419end:
420 return ret;
421}
422
5c1f54d1 423static struct lttng_inode *lttng_inode_create(const struct inode_id *id,
f7c3ffd7
JG
424 struct cds_lfht *ht,
425 struct lttng_unlinked_file_pool *unlinked_file_pool,
426 struct lttng_directory_handle *directory_handle,
427 const char *path)
e0e72660 428{
f7c3ffd7
JG
429 struct lttng_inode *inode = NULL;
430 char *path_copy;
431 bool reference_acquired;
432
433 path_copy = strdup(path);
434 if (!path_copy) {
435 goto end;
436 }
e0e72660 437
f7c3ffd7 438 reference_acquired = lttng_directory_handle_get(directory_handle);
a0377dfe 439 LTTNG_ASSERT(reference_acquired);
f7c3ffd7 440
64803277 441 inode = zmalloc<lttng_inode>();
e0e72660
JG
442 if (!inode) {
443 goto end;
444 }
445
446 urcu_ref_init(&inode->ref);
447 cds_lfht_node_init(&inode->registry_node);
448 inode->id = *id;
e0e72660 449 inode->registry_ht = ht;
f7c3ffd7
JG
450 inode->unlinked_file_pool = unlinked_file_pool;
451 /* Ownership of path copy is transferred to inode. */
452 inode->location.path = path_copy;
453 path_copy = NULL;
454 inode->location.directory_handle = directory_handle;
e0e72660 455end:
f7c3ffd7 456 free(path_copy);
e0e72660 457 return inode;
e0e72660
JG
458}
459
ca806b0b 460struct lttng_inode_registry *lttng_inode_registry_create(void)
e0e72660 461{
64803277 462 struct lttng_inode_registry *registry = zmalloc<lttng_inode_registry>();
e0e72660
JG
463
464 if (!registry) {
465 goto end;
466 }
467
468 pthread_mutex_lock(&seed.lock);
469 if (!seed.initialized) {
470 seed.value = (unsigned long) time(NULL);
471 seed.initialized = true;
472 }
473 pthread_mutex_unlock(&seed.lock);
474
475 registry->inodes = cds_lfht_new(DEFAULT_HT_SIZE, 1, 0,
476 CDS_LFHT_AUTO_RESIZE | CDS_LFHT_ACCOUNTING, NULL);
477 if (!registry->inodes) {
478 goto error;
479 }
480end:
481 return registry;
482error:
483 lttng_inode_registry_destroy(registry);
484 return NULL;
485}
486
ca806b0b 487void lttng_inode_registry_destroy(
938bd422 488 struct lttng_inode_registry *registry)
e0e72660
JG
489{
490 if (!registry) {
491 return;
492 }
493 if (registry->inodes) {
494 int ret = cds_lfht_destroy(registry->inodes, NULL);
495
a0377dfe 496 LTTNG_ASSERT(!ret);
e0e72660
JG
497 }
498 free(registry);
499}
500
ca806b0b 501struct lttng_inode *lttng_inode_registry_get_inode(
f7c3ffd7
JG
502 struct lttng_inode_registry *registry,
503 struct lttng_directory_handle *handle,
504 const char *path,
505 int fd,
506 struct lttng_unlinked_file_pool *unlinked_file_pool)
e0e72660
JG
507{
508 int ret;
509 struct stat statbuf;
510 struct inode_id id;
511 struct cds_lfht_iter iter;
512 struct cds_lfht_node *node;
513 struct lttng_inode *inode = NULL;
514
515 ret = fstat(fd, &statbuf);
516 if (ret < 0) {
f7c3ffd7 517 PERROR("stat() failed on fd %i", fd);
e0e72660
JG
518 goto end;
519 }
520
521 id.device = statbuf.st_dev;
522 id.inode = statbuf.st_ino;
523
524 rcu_read_lock();
5c1f54d1
JG
525 cds_lfht_lookup(registry->inodes, lttng_inode_id_hash(&id),
526 lttng_inode_match, &id, &iter);
e0e72660
JG
527 node = cds_lfht_iter_get_node(&iter);
528 if (node) {
0114db0e
JG
529 inode = lttng::utils::container_of(
530 node, &lttng_inode::registry_node);
e0e72660
JG
531 lttng_inode_get(inode);
532 goto end_unlock;
533 }
534
f7c3ffd7
JG
535 inode = lttng_inode_create(&id, registry->inodes, unlinked_file_pool,
536 handle, path);
640b9481
JR
537 if (!inode) {
538 goto end_unlock;
539 }
540
e0e72660 541 node = cds_lfht_add_unique(registry->inodes,
5c1f54d1
JG
542 lttng_inode_id_hash(&inode->id), lttng_inode_match,
543 &inode->id, &inode->registry_node);
a0377dfe 544 LTTNG_ASSERT(node == &inode->registry_node);
e0e72660
JG
545end_unlock:
546 rcu_read_unlock();
547end:
548 return inode;
549}
This page took 0.066426 seconds and 4 git commands to generate.