Rename C++ header files to .hpp
[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 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 .initialized = false,
70 .value = 0,
71 };
72
73 static unsigned long lttng_inode_id_hash(const struct inode_id *id)
74 {
75 uint64_t device = id->device, inode_no = id->inode;
76
77 return hash_key_u64(&device, seed.value) ^
78 hash_key_u64(&inode_no, seed.value);
79 }
80
81 static int lttng_inode_match(struct cds_lfht_node *node, const void *key)
82 {
83 const struct inode_id *id = (inode_id *) key;
84 const struct lttng_inode *inode = caa_container_of(
85 node, struct lttng_inode, registry_node);
86
87 return inode->id.device == id->device && inode->id.inode == id->inode;
88 }
89
90 static void lttng_inode_free(struct rcu_head *head)
91 {
92 struct lttng_inode *inode =
93 caa_container_of(head, struct lttng_inode, rcu_head);
94
95 free(inode);
96 }
97
98 static 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);
119 LTTNG_ASSERT(!pool->unlink_directory_handle);
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);
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 }
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);
157 LTTNG_ASSERT(reference_acquired);
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++;
165 end:
166 free(inode_unlinked_name);
167 return ret;
168 }
169
170 static 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 }
205 end:
206 return ret;
207 }
208
209 static void lttng_inode_destroy(struct lttng_inode *inode)
210 {
211 if (!inode) {
212 return;
213 }
214
215 rcu_read_lock();
216 cds_lfht_del(inode->registry_ht, &inode->registry_node);
217 rcu_read_unlock();
218
219 if (inode->unlink_pending) {
220 int ret;
221
222 LTTNG_ASSERT(inode->location.directory_handle);
223 LTTNG_ASSERT(inode->location.path);
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);
227 if (ret) {
228 PERROR("Failed to unlink %s", inode->location.path);
229 }
230 }
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);
238 }
239
240 static void lttng_inode_release(struct urcu_ref *ref)
241 {
242 lttng_inode_destroy(caa_container_of(ref, struct lttng_inode, ref));
243 }
244
245 static void lttng_inode_get(struct lttng_inode *inode)
246 {
247 urcu_ref_get(&inode->ref);
248 }
249
250 struct lttng_unlinked_file_pool *lttng_unlinked_file_pool_create(
251 const char *path)
252 {
253 struct lttng_unlinked_file_pool *pool = (lttng_unlinked_file_pool *) zmalloc(sizeof(*pool));
254
255 if (!pool) {
256 goto error;
257 }
258
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;
272 error:
273 lttng_unlinked_file_pool_destroy(pool);
274 return NULL;
275 }
276
277 void lttng_unlinked_file_pool_destroy(
278 struct lttng_unlinked_file_pool *pool)
279 {
280 if (!pool) {
281 return;
282 }
283
284 LTTNG_ASSERT(pool->file_count == 0);
285 lttng_directory_handle_put(pool->unlink_directory_handle);
286 free(pool->unlink_directory_path);
287 free(pool);
288 }
289
290 void lttng_inode_put(struct lttng_inode *inode)
291 {
292 urcu_ref_put(&inode->ref, lttng_inode_release);
293 }
294
295 struct lttng_directory_handle *
296 lttng_inode_get_location_directory_handle(
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
303 LTTNG_ASSERT(reference_acquired);
304 }
305 return inode->location.directory_handle;
306 }
307
308 void lttng_inode_borrow_location(struct lttng_inode *inode,
309 const struct lttng_directory_handle **out_directory_handle,
310 const char **out_path)
311 {
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 }
318 }
319
320 int lttng_inode_rename(
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)
327 {
328 int ret = 0;
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 }
343
344 if (inode->unlink_pending) {
345 WARN("An attempt to rename an unlinked file from %s to %s has been performed",
346 old_path, new_path);
347 ret = -ENOENT;
348 goto end;
349 }
350
351 if (!overwrite) {
352 /* Verify that file doesn't exist. */
353 struct stat statbuf;
354
355 ret = lttng_directory_handle_stat(
356 new_directory_handle, new_path, &statbuf);
357 if (ret == 0) {
358 ERR("Refusing to rename %s as the destination already exists",
359 old_path);
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
369 ret = lttng_directory_handle_rename(old_directory_handle, old_path,
370 new_directory_handle, new_path);
371 if (ret) {
372 PERROR("Failed to rename file %s to %s", old_path, new_path);
373 ret = -errno;
374 goto end;
375 }
376
377 reference_acquired = lttng_directory_handle_get(new_directory_handle);
378 LTTNG_ASSERT(reference_acquired);
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;
384 new_path_copy = NULL;
385 end:
386 free(new_path_copy);
387 return ret;
388 }
389
390 int lttng_inode_unlink(struct lttng_inode *inode)
391 {
392 int ret = 0;
393
394 DBG("Attempting unlink of inode %s", inode->location.path);
395
396 if (inode->unlink_pending) {
397 WARN("An attempt to re-unlink %s has been performed, ignoring.",
398 inode->location.path);
399 ret = -ENOENT;
400 goto end;
401 }
402
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);
412 goto end;
413 }
414 inode->unlink_pending = true;
415 end:
416 return ret;
417 }
418
419 static struct lttng_inode *lttng_inode_create(const struct inode_id *id,
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)
424 {
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 }
433
434 reference_acquired = lttng_directory_handle_get(directory_handle);
435 LTTNG_ASSERT(reference_acquired);
436
437 inode = (lttng_inode *) zmalloc(sizeof(*inode));
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;
445 inode->registry_ht = ht;
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;
451 end:
452 free(path_copy);
453 return inode;
454 }
455
456 struct lttng_inode_registry *lttng_inode_registry_create(void)
457 {
458 struct lttng_inode_registry *registry = (lttng_inode_registry *) zmalloc(sizeof(*registry));
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 }
476 end:
477 return registry;
478 error:
479 lttng_inode_registry_destroy(registry);
480 return NULL;
481 }
482
483 void lttng_inode_registry_destroy(
484 struct lttng_inode_registry *registry)
485 {
486 if (!registry) {
487 return;
488 }
489 if (registry->inodes) {
490 int ret = cds_lfht_destroy(registry->inodes, NULL);
491
492 LTTNG_ASSERT(!ret);
493 }
494 free(registry);
495 }
496
497 struct lttng_inode *lttng_inode_registry_get_inode(
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)
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) {
513 PERROR("stat() failed on fd %i", fd);
514 goto end;
515 }
516
517 id.device = statbuf.st_dev;
518 id.inode = statbuf.st_ino;
519
520 rcu_read_lock();
521 cds_lfht_lookup(registry->inodes, lttng_inode_id_hash(&id),
522 lttng_inode_match, &id, &iter);
523 node = cds_lfht_iter_get_node(&iter);
524 if (node) {
525 inode = caa_container_of(
526 node, struct lttng_inode, registry_node);
527 lttng_inode_get(inode);
528 goto end_unlock;
529 }
530
531 inode = lttng_inode_create(&id, registry->inodes, unlinked_file_pool,
532 handle, path);
533 if (!inode) {
534 goto end_unlock;
535 }
536
537 node = cds_lfht_add_unique(registry->inodes,
538 lttng_inode_id_hash(&inode->id), lttng_inode_match,
539 &inode->id, &inode->registry_node);
540 LTTNG_ASSERT(node == &inode->registry_node);
541 end_unlock:
542 rcu_read_unlock();
543 end:
544 return inode;
545 }
This page took 0.041069 seconds and 4 git commands to generate.