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