fd-tracker: restore suspended handles from their inode's path
[lttng-tools.git] / src / common / fd-tracker / inode.c
1 /*
2 * Copyright (C) 2020 - Jérémie Galarneau <jeremie.galarneau@efficios.com>
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
18 #include <common/defaults.h>
19 #include <common/error.h>
20 #include <common/hashtable/utils.h>
21 #include <common/macros.h>
22 #include <common/optional.h>
23 #include <common/string-utils/format.h>
24 #include <common/utils.h>
25 #include <inttypes.h>
26 #include <lttng/constant.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <urcu.h>
31 #include <urcu/rculfhash.h>
32 #include <urcu/ref.h>
33
34 #include "inode.h"
35
36 struct inode_id {
37 dev_t device;
38 ino_t inode;
39 };
40
41 struct lttng_inode_registry {
42 /* Hashtable of inode_id to lttng_inode. */
43 struct cds_lfht *inodes;
44 };
45
46 struct lttng_inode {
47 struct inode_id id;
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;
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
66 struct 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;
71 };
72
73 static struct {
74 pthread_mutex_t lock;
75 bool initialized;
76 unsigned long value;
77 } seed = {
78 .lock = PTHREAD_MUTEX_INITIALIZER,
79 };
80
81 static unsigned long lttng_inode_id_hash(const struct inode_id *id)
82 {
83 uint64_t device = id->device, inode_no = id->inode;
84
85 return hash_key_u64(&device, seed.value) ^
86 hash_key_u64(&inode_no, seed.value);
87 }
88
89 static int lttng_inode_match(struct cds_lfht_node *node, const void *key)
90 {
91 const struct inode_id *id = key;
92 const struct lttng_inode *inode = caa_container_of(
93 node, struct lttng_inode, registry_node);
94
95 return inode->id.device == id->device && inode->id.inode == id->inode;
96 }
97
98 static void lttng_inode_free(struct rcu_head *head)
99 {
100 struct lttng_inode *inode =
101 caa_container_of(head, struct lttng_inode, rcu_head);
102
103 free(inode);
104 }
105
106 static 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++;
167 end:
168 free(inode_unlinked_name);
169 return ret;
170 }
171
172 static 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 }
207 end:
208 return ret;
209 }
210
211 static void lttng_inode_destroy(struct lttng_inode *inode)
212 {
213 if (!inode) {
214 return;
215 }
216
217 rcu_read_lock();
218 cds_lfht_del(inode->registry_ht, &inode->registry_node);
219 rcu_read_unlock();
220
221 if (inode->unlink_pending) {
222 int ret;
223
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);
229 if (ret) {
230 PERROR("Failed to unlink %s", inode->location.path);
231 }
232 }
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);
240 }
241
242 static void lttng_inode_release(struct urcu_ref *ref)
243 {
244 lttng_inode_destroy(caa_container_of(ref, struct lttng_inode, ref));
245 }
246
247 static void lttng_inode_get(struct lttng_inode *inode)
248 {
249 urcu_ref_get(&inode->ref);
250 }
251
252 struct 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;
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 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 void lttng_inode_get_location(struct lttng_inode *inode,
294 const struct lttng_directory_handle **out_directory_handle,
295 const char **out_path)
296 {
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 }
303 }
304
305 int lttng_inode_rename(
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)
312 {
313 int ret = 0;
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 }
328
329 if (inode->unlink_pending) {
330 WARN("An attempt to rename an unlinked file from %s to %s has been performed",
331 old_path, new_path);
332 ret = -ENOENT;
333 goto end;
334 }
335
336 if (!overwrite) {
337 /* Verify that file doesn't exist. */
338 struct stat statbuf;
339
340 ret = lttng_directory_handle_stat(
341 new_directory_handle, new_path, &statbuf);
342 if (ret == 0) {
343 ERR("Refusing to rename %s as the destination already exists",
344 old_path);
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
354 ret = lttng_directory_handle_rename(old_directory_handle, old_path,
355 new_directory_handle, new_path);
356 if (ret) {
357 PERROR("Failed to rename file %s to %s", old_path, new_path);
358 ret = -errno;
359 goto end;
360 }
361
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;
369 new_path_copy = NULL;
370 end:
371 free(new_path_copy);
372 return ret;
373 }
374
375 int lttng_inode_unlink(struct lttng_inode *inode)
376 {
377 int ret = 0;
378
379 DBG("Attempting unlink of inode %s", inode->location.path);
380
381 if (inode->unlink_pending) {
382 WARN("An attempt to re-unlink %s has been performed, ignoring.",
383 inode->location.path);
384 ret = -ENOENT;
385 goto end;
386 }
387
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);
397 goto end;
398 }
399 inode->unlink_pending = true;
400 end:
401 return ret;
402 }
403
404 static struct lttng_inode *lttng_inode_create(const struct inode_id *id,
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)
409 {
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 }
418
419 reference_acquired = lttng_directory_handle_get(directory_handle);
420 assert(reference_acquired);
421
422 inode = zmalloc(sizeof(*inode));
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;
430 inode->registry_ht = ht;
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;
436 end:
437 free(path_copy);
438 return inode;
439 }
440
441 struct 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 }
461 end:
462 return registry;
463 error:
464 lttng_inode_registry_destroy(registry);
465 return NULL;
466 }
467
468 void 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
481 struct lttng_inode *lttng_inode_registry_get_inode(
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)
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) {
497 PERROR("stat() failed on fd %i", fd);
498 goto end;
499 }
500
501 id.device = statbuf.st_dev;
502 id.inode = statbuf.st_ino;
503
504 rcu_read_lock();
505 cds_lfht_lookup(registry->inodes, lttng_inode_id_hash(&id),
506 lttng_inode_match, &id, &iter);
507 node = cds_lfht_iter_get_node(&iter);
508 if (node) {
509 inode = caa_container_of(
510 node, struct lttng_inode, registry_node);
511 lttng_inode_get(inode);
512 goto end_unlock;
513 }
514
515 inode = lttng_inode_create(&id, registry->inodes, unlinked_file_pool,
516 handle, path);
517 node = cds_lfht_add_unique(registry->inodes,
518 lttng_inode_id_hash(&inode->id), lttng_inode_match,
519 &inode->id, &inode->registry_node);
520 assert(node == &inode->registry_node);
521 end_unlock:
522 rcu_read_unlock();
523 end:
524 return inode;
525 }
This page took 0.039202 seconds and 4 git commands to generate.