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