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