Fix: futex wait: handle spurious futex wakeups
[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.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>
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.hpp"
25
26 namespace {
27 struct inode_id {
28 dev_t device;
29 ino_t inode;
30 };
31 } /* namespace */
32
33 struct lttng_inode_registry {
34 /* Hashtable of inode_id to lttng_inode. */
35 struct cds_lfht *inodes;
36 };
37
38 struct lttng_inode {
39 struct inode_id id;
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;
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
58 struct 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;
63 };
64
65 namespace {
66 struct {
67 pthread_mutex_t lock;
68 bool initialized;
69 unsigned long value;
70 } seed = {
71 .lock = PTHREAD_MUTEX_INITIALIZER,
72 .initialized = false,
73 .value = 0,
74 };
75 } /* namespace */
76
77 static unsigned long lttng_inode_id_hash(const struct inode_id *id)
78 {
79 uint64_t device = id->device, inode_no = id->inode;
80
81 return hash_key_u64(&device, seed.value) ^
82 hash_key_u64(&inode_no, seed.value);
83 }
84
85 static int lttng_inode_match(struct cds_lfht_node *node, const void *key)
86 {
87 const struct inode_id *id = (inode_id *) key;
88 const struct lttng_inode *inode = lttng::utils::container_of(
89 node, &lttng_inode::registry_node);
90
91 return inode->id.device == id->device && inode->id.inode == id->inode;
92 }
93
94 static void lttng_inode_free(struct rcu_head *head)
95 {
96 struct lttng_inode *inode =
97 lttng::utils::container_of(head, &lttng_inode::rcu_head);
98
99 free(inode);
100 }
101
102 static 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);
123 LTTNG_ASSERT(!pool->unlink_directory_handle);
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);
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 }
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);
161 LTTNG_ASSERT(reference_acquired);
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++;
169 end:
170 free(inode_unlinked_name);
171 return ret;
172 }
173
174 static 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 }
209 end:
210 return ret;
211 }
212
213 static void lttng_inode_destroy(struct lttng_inode *inode)
214 {
215 if (!inode) {
216 return;
217 }
218
219 rcu_read_lock();
220 cds_lfht_del(inode->registry_ht, &inode->registry_node);
221 rcu_read_unlock();
222
223 if (inode->unlink_pending) {
224 int ret;
225
226 LTTNG_ASSERT(inode->location.directory_handle);
227 LTTNG_ASSERT(inode->location.path);
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);
231 if (ret) {
232 PERROR("Failed to unlink %s", inode->location.path);
233 }
234 }
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);
242 }
243
244 static void lttng_inode_release(struct urcu_ref *ref)
245 {
246 lttng_inode_destroy(lttng::utils::container_of(ref, &lttng_inode::ref));
247 }
248
249 static void lttng_inode_get(struct lttng_inode *inode)
250 {
251 urcu_ref_get(&inode->ref);
252 }
253
254 struct lttng_unlinked_file_pool *lttng_unlinked_file_pool_create(
255 const char *path)
256 {
257 struct lttng_unlinked_file_pool *pool = zmalloc<lttng_unlinked_file_pool>();
258
259 if (!pool) {
260 goto error;
261 }
262
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;
276 error:
277 lttng_unlinked_file_pool_destroy(pool);
278 return NULL;
279 }
280
281 void lttng_unlinked_file_pool_destroy(
282 struct lttng_unlinked_file_pool *pool)
283 {
284 if (!pool) {
285 return;
286 }
287
288 LTTNG_ASSERT(pool->file_count == 0);
289 lttng_directory_handle_put(pool->unlink_directory_handle);
290 free(pool->unlink_directory_path);
291 free(pool);
292 }
293
294 void lttng_inode_put(struct lttng_inode *inode)
295 {
296 urcu_ref_put(&inode->ref, lttng_inode_release);
297 }
298
299 struct lttng_directory_handle *
300 lttng_inode_get_location_directory_handle(
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
307 LTTNG_ASSERT(reference_acquired);
308 }
309 return inode->location.directory_handle;
310 }
311
312 void lttng_inode_borrow_location(struct lttng_inode *inode,
313 const struct lttng_directory_handle **out_directory_handle,
314 const char **out_path)
315 {
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 }
322 }
323
324 int lttng_inode_rename(
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)
331 {
332 int ret = 0;
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 }
347
348 if (inode->unlink_pending) {
349 WARN("An attempt to rename an unlinked file from %s to %s has been performed",
350 old_path, new_path);
351 ret = -ENOENT;
352 goto end;
353 }
354
355 if (!overwrite) {
356 /* Verify that file doesn't exist. */
357 struct stat statbuf;
358
359 ret = lttng_directory_handle_stat(
360 new_directory_handle, new_path, &statbuf);
361 if (ret == 0) {
362 ERR("Refusing to rename %s as the destination already exists",
363 old_path);
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
373 ret = lttng_directory_handle_rename(old_directory_handle, old_path,
374 new_directory_handle, new_path);
375 if (ret) {
376 PERROR("Failed to rename file %s to %s", old_path, new_path);
377 ret = -errno;
378 goto end;
379 }
380
381 reference_acquired = lttng_directory_handle_get(new_directory_handle);
382 LTTNG_ASSERT(reference_acquired);
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;
388 new_path_copy = NULL;
389 end:
390 free(new_path_copy);
391 return ret;
392 }
393
394 int lttng_inode_unlink(struct lttng_inode *inode)
395 {
396 int ret = 0;
397
398 DBG("Attempting unlink of inode %s", inode->location.path);
399
400 if (inode->unlink_pending) {
401 WARN("An attempt to re-unlink %s has been performed, ignoring.",
402 inode->location.path);
403 ret = -ENOENT;
404 goto end;
405 }
406
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);
416 goto end;
417 }
418 inode->unlink_pending = true;
419 end:
420 return ret;
421 }
422
423 static struct lttng_inode *lttng_inode_create(const struct inode_id *id,
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)
428 {
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 }
437
438 reference_acquired = lttng_directory_handle_get(directory_handle);
439 LTTNG_ASSERT(reference_acquired);
440
441 inode = zmalloc<lttng_inode>();
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;
449 inode->registry_ht = ht;
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;
455 end:
456 free(path_copy);
457 return inode;
458 }
459
460 struct lttng_inode_registry *lttng_inode_registry_create(void)
461 {
462 struct lttng_inode_registry *registry = zmalloc<lttng_inode_registry>();
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 }
480 end:
481 return registry;
482 error:
483 lttng_inode_registry_destroy(registry);
484 return NULL;
485 }
486
487 void lttng_inode_registry_destroy(
488 struct lttng_inode_registry *registry)
489 {
490 if (!registry) {
491 return;
492 }
493 if (registry->inodes) {
494 int ret = cds_lfht_destroy(registry->inodes, NULL);
495
496 LTTNG_ASSERT(!ret);
497 }
498 free(registry);
499 }
500
501 struct lttng_inode *lttng_inode_registry_get_inode(
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)
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) {
517 PERROR("stat() failed on fd %i", fd);
518 goto end;
519 }
520
521 id.device = statbuf.st_dev;
522 id.inode = statbuf.st_ino;
523
524 rcu_read_lock();
525 cds_lfht_lookup(registry->inodes, lttng_inode_id_hash(&id),
526 lttng_inode_match, &id, &iter);
527 node = cds_lfht_iter_get_node(&iter);
528 if (node) {
529 inode = lttng::utils::container_of(
530 node, &lttng_inode::registry_node);
531 lttng_inode_get(inode);
532 goto end_unlock;
533 }
534
535 inode = lttng_inode_create(&id, registry->inodes, unlinked_file_pool,
536 handle, path);
537 if (!inode) {
538 goto end_unlock;
539 }
540
541 node = cds_lfht_add_unique(registry->inodes,
542 lttng_inode_id_hash(&inode->id), lttng_inode_match,
543 &inode->id, &inode->registry_node);
544 LTTNG_ASSERT(node == &inode->registry_node);
545 end_unlock:
546 rcu_read_unlock();
547 end:
548 return inode;
549 }
This page took 0.039815 seconds and 4 git commands to generate.