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