fd-tracker: restore suspended handles from their inode's path
[lttng-tools.git] / tests / unit / test_fd_tracker.c
CommitLineData
9ca3e8a2
JG
1/*
2 * Copyright (c) - 2018 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 as published by as
6 * published by the Free Software Foundation; only version 2 of the License.
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 <stdlib.h>
19#include <inttypes.h>
20#include <stdbool.h>
21#include <assert.h>
22#include <string.h>
23#include <stdarg.h>
24#include <tap/tap.h>
25#include <sys/types.h>
26#include <dirent.h>
27#include <stdio.h>
28#include <errno.h>
29#include <unistd.h>
30#include <fcntl.h>
31#include <sys/stat.h>
f0faf175 32#include <sys/types.h>
9ca3e8a2
JG
33
34#include <urcu.h>
35
f7c3ffd7 36#include <common/compat/directory-handle.h>
9ca3e8a2 37#include <common/error.h>
f7c3ffd7 38#include <common/fd-tracker/fd-tracker.h>
9ca3e8a2
JG
39
40/* For error.h */
41int lttng_opt_quiet = 1;
42int lttng_opt_verbose;
43int lttng_opt_mi;
44
45/* Number of TAP tests in this file */
f7c3ffd7 46#define NUM_TESTS 61
9ca3e8a2
JG
47/* 3 for stdin, stdout, and stderr */
48#define STDIO_FD_COUNT 3
49#define TRACKER_FD_LIMIT 50
50#define TMP_DIR_PATTERN "/tmp/fd-tracker-XXXXXX"
f7c3ffd7 51#define TEST_UNLINK_DIRECTORY_NAME "unlinked_files"
9ca3e8a2
JG
52
53/*
54 * Count of fds, beyond stdin, stderr, stdout that were open
55 * at the launch of the test. This allows the test to succeed when
56 * run by automake's test runner or valgrind which both open
57 * fds behind our back.
58 */
59int unknown_fds_count;
60
61const char file_contents[] = "Bacon ipsum dolor amet jerky drumstick sirloin "
62 "strip steak venison boudin filet mignon picanha doner shoulder. "
63 "Strip steak brisket alcatra, venison beef chuck cupim pastrami. "
64 "Landjaeger tri-tip salami leberkas ball tip, ham hock chuck sausage "
65 "flank jerky cupim. Pig bacon chuck pancetta andouille.";
66
f7c3ffd7
JG
67void get_temporary_directories(char **_test_directory, char **_unlink_directory)
68{
69 int ret;
70 char tmp_path_pattern[] = TMP_DIR_PATTERN;
71 char *output_dir;
72
73 output_dir = mkdtemp(tmp_path_pattern);
74 if (!output_dir) {
75 diag("Failed to create temporary path of the form %s",
76 TMP_DIR_PATTERN);
77 assert(0);
78 }
79
80 *_test_directory = strdup(output_dir);
81 assert(*_test_directory);
82 ret = asprintf(_unlink_directory, "%s/%s", output_dir,
83 TEST_UNLINK_DIRECTORY_NAME);
84 if (ret < 0) {
85 assert(0);
86 }
87}
88
9ca3e8a2
JG
89int fd_count(void)
90{
91 DIR *dir;
92 struct dirent *entry;
93 int count = 0;
94
95 dir = opendir("/proc/self/fd");
96 if (!dir) {
97 perror("# Failed to enumerate /proc/self/fd/ to count the number of used file descriptors");
98 count = -1;
99 goto end;
100 }
101
102 while ((entry = readdir(dir)) != NULL) {
103 if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) {
104 continue;
105 }
106 count++;
107 }
108 /* Don't account for the file descriptor opened by opendir(). */
109 count--;
e0efb2c8
JG
110 if (closedir(dir)) {
111 perror("# Failed to close test program's self/fd directory file descriptor");
112 }
9ca3e8a2
JG
113end:
114 return count;
115}
116
117static
118void check_fd_count(int expected_count)
119{
120 int count = 0;
121
122 count = fd_count();
123 ok(count == expected_count, "Expected %d open file descriptors (%d are open)",
124 expected_count, count);
125}
126
127static
128int noop_open(void *data, int *fds)
129{
130 *fds = *((int *) data);
131 return 0;
132}
133
134static
135int noop_close(void *data, int *fds)
136{
137 return 0;
138}
139
140static
141void track_std_fds(struct fd_tracker *tracker)
142{
143 int i;
144 struct { int fd; const char *name; } files[] = {
145 { .fd = fileno(stdin), .name = "stdin" },
146 { .fd = fileno(stdout), .name = "stdout" },
147 { .fd = fileno(stderr), .name = "stderr" },
148 };
149
150 for (i = 0; i < sizeof(files) / sizeof(*files); i++) {
151 int out_fd, ret;
152
153 ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd,
154 &files[i].name, 1, noop_open, &files[i].fd);
155 assert(out_fd == files[i].fd);
156
157 ok(ret == 0, "Track unsuspendable fd %d (%s)", files[i].fd,
158 files[i].name);
159 }
160}
161
162static
163void untrack_std_fds(struct fd_tracker *tracker)
164{
165 int i;
166 struct { int fd; const char *name; } files[] = {
167 { .fd = fileno(stdin), .name = "stdin" },
168 { .fd = fileno(stdout), .name = "stdout" },
169 { .fd = fileno(stderr), .name = "stderr" },
170 };
171 unsigned int fds_set_to_minus_1 = 0;
172
173 for (i = 0; i < sizeof(files) / sizeof(*files); i++) {
174 int fd = files[i].fd;
175 int ret = fd_tracker_close_unsuspendable_fd(tracker,
176 &files[i].fd, 1, noop_close, NULL);
177
178 ok(ret == 0, "Untrack unsuspendable fd %d (%s)", fd,
179 files[i].name);
180 fds_set_to_minus_1 += (files[i].fd == -1);
181 }
182}
183
184/*
185 * Basic test opening and closing three unsuspendable fds.
186 */
187static
188void test_unsuspendable_basic(void)
189{
f7c3ffd7 190 int ret;
9ca3e8a2 191 struct fd_tracker *tracker;
f7c3ffd7
JG
192 char *test_directory = NULL, *unlinked_files_directory = NULL;
193
194 get_temporary_directories(&test_directory, &unlinked_files_directory);
9ca3e8a2 195
f7c3ffd7 196 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
9ca3e8a2
JG
197 ok(tracker, "Created an fd tracker with a limit of %d simulateously opened file descriptors",
198 TRACKER_FD_LIMIT);
199 if (!tracker) {
200 return;
201 }
202
203 track_std_fds(tracker);
204 untrack_std_fds(tracker);
205
206 fd_tracker_destroy(tracker);
f7c3ffd7
JG
207 ret = rmdir(test_directory);
208 ok(ret == 0, "Test directory is empty");
209 free(test_directory);
210 free(unlinked_files_directory);
9ca3e8a2
JG
211}
212
213static
214int error_open(void *data, int *fds)
215{
216 return *((int *) data);
217}
218
219static
220int error_close(void *data, int *fds)
221{
222 return *((int *) data);
223}
224
225/*
226 * Validate that user callback return values are returned to the
227 * caller of the fd tracker.
228 */
229static
230void test_unsuspendable_cb_return(void)
231{
232 int ret, stdout_fd = fileno(stdout), out_fd = 42;
233 struct fd_tracker *tracker;
234 int expected_error = -ENETDOWN;
f7c3ffd7 235 char *test_directory = NULL, *unlinked_files_directory = NULL;
9ca3e8a2 236
f7c3ffd7
JG
237 get_temporary_directories(&test_directory, &unlinked_files_directory);
238
239 tracker = fd_tracker_create(test_directory, TRACKER_FD_LIMIT);
9ca3e8a2
JG
240 assert(tracker);
241
242 /* The error_open callback should fail and return 'expected_error'. */
243 ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd,
244 NULL, 1, error_open, &expected_error);
245 ok(ret == expected_error, "fd_tracker_open_unsuspendable_fd() forwards the user callback's error code");
246 ok(out_fd == 42, "Output fd parameter is unaffected on error of fd_tracker_open_unsuspendable_fd()");
247
248 /*
249 * Track a valid fd since we don't want the tracker to fail with an
250 * invalid fd error for this test.
251 */
252 ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd,
253 NULL, 1, noop_open, &stdout_fd);
254 ok(out_fd == stdout_fd, "fd_tracker_open_unsuspendable_fd() sets the output fd parameter to the newly-tracked fd's value");
255 assert(!ret);
256
257 ret = fd_tracker_close_unsuspendable_fd(tracker,
258 &stdout_fd, 1, error_close, &expected_error);
259 ok(ret == expected_error, "fd_tracker_close_unsuspendable_fd() forwards the user callback's error code");
260 ret = fd_tracker_close_unsuspendable_fd(tracker,
261 &stdout_fd, 1, noop_close, &expected_error);
262 assert(!ret);
263
264 fd_tracker_destroy(tracker);
f7c3ffd7
JG
265 ret = rmdir(test_directory);
266 ok(ret == 0, "Test directory is empty");
267 free(test_directory);
268 free(unlinked_files_directory);
9ca3e8a2
JG
269}
270
271/*
272 * Validate that the tracker refuses to track two identical unsuspendable
273 * file descriptors.
274 */
275static
276void test_unsuspendable_duplicate(void)
277{
278 int ret, stdout_fd = fileno(stdout), out_fd;
279 struct fd_tracker *tracker;
f7c3ffd7
JG
280 char *test_directory = NULL, *unlinked_files_directory = NULL;
281
282 get_temporary_directories(&test_directory, &unlinked_files_directory);
9ca3e8a2 283
f7c3ffd7 284 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
9ca3e8a2
JG
285 assert(tracker);
286
287 ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd,
288 NULL, 1, noop_open, &stdout_fd);
289 assert(!ret);
290 ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd,
291 NULL, 1, noop_open, &stdout_fd);
292 ok(ret == -EEXIST, "EEXIST reported on open of an already tracked file descriptor");
293
294 ret = fd_tracker_close_unsuspendable_fd(tracker,
295 &stdout_fd, 1, noop_close, NULL);
296 assert(!ret);
297
298 fd_tracker_destroy(tracker);
f7c3ffd7
JG
299 ret = rmdir(test_directory);
300 ok(ret == 0, "Test directory is empty");
301 free(test_directory);
302 free(unlinked_files_directory);
9ca3e8a2
JG
303}
304
305static
306int open_pipes(void *data, int *out_fds)
307{
308 unsigned int i;
309 const unsigned int pipe_count = TRACKER_FD_LIMIT / 2;
310
311 for (i = 0; i < pipe_count; i++) {
312 int ret = pipe(&out_fds[i * 2]);
313
314 if (ret) {
315 return -errno;
316 }
317 }
318 return 0;
319}
320
321static
322int close_pipes(void *data, int *fds)
323{
324 int i;
325 int *pipes = fds;
326
327 for (i = 0; i < TRACKER_FD_LIMIT; i++) {
328 int ret = close(pipes[i]);
329
330 if (ret) {
331 return -errno;
332 }
333 }
334 return 0;
335}
336
337/*
338 * Validate that the tracker enforces the open file descriptor limit
339 * when unsuspendable file descritptors are being opened.
340 */
341static
342void test_unsuspendable_limit(void)
343{
344 struct fd_tracker *tracker;
345 int ret, stdout_fd = fileno(stdout), out_fd;
346 int fds[TRACKER_FD_LIMIT];
f7c3ffd7
JG
347 char *test_directory = NULL, *unlinked_files_directory = NULL;
348
349 get_temporary_directories(&test_directory, &unlinked_files_directory);
9ca3e8a2
JG
350
351 /* This test assumes TRACKER_FD_LIMIT is a multiple of 2. */
352 assert((TRACKER_FD_LIMIT % 2 == 0) && TRACKER_FD_LIMIT);
353
f7c3ffd7 354 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
9ca3e8a2
JG
355 assert(tracker);
356
357 ret = fd_tracker_open_unsuspendable_fd(tracker, fds,
358 NULL, TRACKER_FD_LIMIT, open_pipes, NULL);
359 ok(ret == 0, "File descriptor tracker allowed the user to meet its limit with unsuspendable file descritptors (%d)",
360 TRACKER_FD_LIMIT);
361
362 ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd,
363 NULL, 1, noop_open, &stdout_fd);
364 ok(ret == -EMFILE, "EMFILE reported when exceeding the file descriptor limit while opening an unsuspendable fd");
365
366 ret = fd_tracker_close_unsuspendable_fd(tracker,
367 fds, TRACKER_FD_LIMIT, close_pipes, NULL);
368 assert(!ret);
369
370 fd_tracker_destroy(tracker);
f7c3ffd7
JG
371 ret = rmdir(test_directory);
372 ok(ret == 0, "Test directory is empty");
373 free(test_directory);
374 free(unlinked_files_directory);
9ca3e8a2
JG
375}
376
377/*
378 * Validate that the tracker refuses to track two identical unsuspendable
379 * file descriptors.
380 */
381static
382void test_unsuspendable_close_untracked(void)
383{
384 int ret, stdout_fd = fileno(stdout), unknown_fds[2], out_fd;
385 struct fd_tracker *tracker;
f7c3ffd7 386 char *test_directory = NULL, *unlinked_files_directory = NULL;
9ca3e8a2 387
f7c3ffd7
JG
388 get_temporary_directories(&test_directory, &unlinked_files_directory);
389
390 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
9ca3e8a2
JG
391 if (!tracker) {
392 return;
393 }
394
395 ret = pipe(unknown_fds);
396 assert(!ret);
397 assert(close(unknown_fds[0]) == 0);
398 assert(close(unknown_fds[1]) == 0);
399
400 ret = fd_tracker_open_unsuspendable_fd(tracker, &out_fd,
401 NULL, 1, noop_open, &stdout_fd);
402 assert(!ret);
403
404 ret = fd_tracker_close_unsuspendable_fd(tracker,
405 unknown_fds, 1, noop_close, NULL);
406 ok(ret == -EINVAL, "EINVAL reported on close of an untracked file descriptor");
407
408 ret = fd_tracker_close_unsuspendable_fd(tracker,
409 &stdout_fd, 1, noop_close, NULL);
410 assert(!ret);
411
412 fd_tracker_destroy(tracker);
f7c3ffd7
JG
413 ret = rmdir(test_directory);
414 ok(ret == 0, "Test directory is empty");
415 free(test_directory);
416 free(unlinked_files_directory);
9ca3e8a2
JG
417}
418
f7c3ffd7
JG
419static int open_files(struct fd_tracker *tracker,
420 struct lttng_directory_handle *directory,
421 unsigned int count,
422 struct fs_handle **handles,
423 char **file_paths)
9ca3e8a2
JG
424{
425 int ret = 0;
426 unsigned int i;
427
428 for (i = 0; i < count; i++) {
f0faf175 429 int p_ret;
9ca3e8a2
JG
430 char *file_path;
431 struct fs_handle *handle;
432 mode_t mode = S_IWUSR | S_IRUSR;
433
f7c3ffd7 434 p_ret = asprintf(&file_path, "file-%u", i);
f0faf175 435 assert(p_ret >= 0);
9ca3e8a2
JG
436 file_paths[i] = file_path;
437
f7c3ffd7 438 handle = fd_tracker_open_fs_handle(tracker, directory, file_path,
9ca3e8a2
JG
439 O_RDWR | O_CREAT, &mode);
440 if (!handle) {
441 ret = -1;
442 break;
443 }
444 handles[i] = handle;
445 }
446 return ret;
447}
448
f7c3ffd7
JG
449static int open_same_file(struct fd_tracker *tracker,
450 struct lttng_directory_handle *directory,
451 const char *file,
452 unsigned int count,
453 struct fs_handle **handles)
f0faf175
JG
454{
455 int ret = 0;
456 unsigned int i;
457
458 for (i = 0; i < count; i++) {
459 struct fs_handle *handle;
460 mode_t mode = S_IWUSR | S_IRUSR;
461
f7c3ffd7 462 handle = fd_tracker_open_fs_handle(tracker, directory, file,
f0faf175
JG
463 O_RDWR | O_CREAT, &mode);
464 if (!handle) {
465 ret = -1;
466 break;
467 }
468 handles[i] = handle;
469 }
470 return ret;
471}
472
9ca3e8a2
JG
473static
474int cleanup_files(struct fd_tracker *tracker, const char *dir,
475 unsigned int count, struct fs_handle **handles,
476 char **file_paths)
477{
478 int ret = 0;
479 unsigned int i;
480
481 for (i = 0; i < count; i++) {
482 char *file_path = file_paths[i];
483
484 if (!file_path) {
485 break;
486 }
f7c3ffd7
JG
487 if (fs_handle_unlink(handles[i])) {
488 diag("Failed to unlink fs_handle to file %s", file_path);
489 ret = -1;
490 }
9ca3e8a2 491 if (fs_handle_close(handles[i])) {
f7c3ffd7 492 diag("Failed to close fs_handle to file %s", file_path);
9ca3e8a2
JG
493 ret = -1;
494 }
f7c3ffd7 495 free(file_path);
9ca3e8a2
JG
496 }
497 return ret;
498}
499
500static
501void test_suspendable_limit(void)
502{
503 int ret;
504 const int files_to_create = TRACKER_FD_LIMIT * 10;
505 struct fd_tracker *tracker;
f7c3ffd7 506 char *test_directory = NULL, *unlinked_files_directory = NULL;
9ca3e8a2
JG
507 char *output_files[files_to_create];
508 struct fs_handle *handles[files_to_create];
f7c3ffd7
JG
509 struct lttng_directory_handle *dir_handle = NULL;
510 int dir_handle_fd_count;
9ca3e8a2
JG
511
512 memset(output_files, 0, sizeof(output_files));
513 memset(handles, 0, sizeof(handles));
514
f7c3ffd7
JG
515 get_temporary_directories(&test_directory, &unlinked_files_directory);
516
517 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
9ca3e8a2
JG
518 if (!tracker) {
519 return;
520 }
521
f7c3ffd7
JG
522 dir_handle = lttng_directory_handle_create(test_directory);
523 assert(dir_handle);
524 dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle);
9ca3e8a2 525
f7c3ffd7 526 ret = open_files(tracker, dir_handle, files_to_create, handles,
9ca3e8a2
JG
527 output_files);
528 ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor",
529 files_to_create, TRACKER_FD_LIMIT);
f7c3ffd7
JG
530 check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
531 dir_handle_fd_count);
9ca3e8a2 532
f7c3ffd7 533 ret = cleanup_files(tracker, test_directory, files_to_create, handles,
9ca3e8a2
JG
534 output_files);
535 ok(!ret, "Close all opened filesystem handles");
f7c3ffd7
JG
536 ret = rmdir(test_directory);
537 ok(ret == 0, "Test directory is empty");
9ca3e8a2 538 fd_tracker_destroy(tracker);
f7c3ffd7
JG
539 lttng_directory_handle_put(dir_handle);
540 free(test_directory);
541 free(unlinked_files_directory);
9ca3e8a2
JG
542}
543
544static
545void test_mixed_limit(void)
546{
547 int ret;
548 const int files_to_create = TRACKER_FD_LIMIT;
549 struct fd_tracker *tracker;
f7c3ffd7 550 char *test_directory = NULL, *unlinked_files_directory = NULL;
9ca3e8a2
JG
551 char *output_files[files_to_create];
552 struct fs_handle *handles[files_to_create];
f7c3ffd7
JG
553 struct lttng_directory_handle *dir_handle = NULL;
554 int dir_handle_fd_count;
9ca3e8a2
JG
555
556 memset(output_files, 0, sizeof(output_files));
557 memset(handles, 0, sizeof(handles));
558
f7c3ffd7
JG
559 get_temporary_directories(&test_directory, &unlinked_files_directory);
560
561 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
9ca3e8a2
JG
562 if (!tracker) {
563 return;
564 }
565
f7c3ffd7
JG
566 dir_handle = lttng_directory_handle_create(test_directory);
567 assert(dir_handle);
568 dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle);
9ca3e8a2 569
f7c3ffd7 570 ret = open_files(tracker, dir_handle, files_to_create, handles,
9ca3e8a2
JG
571 output_files);
572 ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor",
573 files_to_create, TRACKER_FD_LIMIT);
574 diag("Check file descriptor count after opening %u files", files_to_create);
f7c3ffd7
JG
575 check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
576 dir_handle_fd_count);
9ca3e8a2
JG
577
578 /*
579 * Open unsuspendable fds (stdin, stdout, stderr) and verify that the fd
580 * cap is still respected.
581 */
582 diag("Check file descriptor count after adding %d unsuspendable fds",
583 STDIO_FD_COUNT);
584 track_std_fds(tracker);
f7c3ffd7
JG
585 check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count +
586 dir_handle_fd_count);
9ca3e8a2
JG
587 diag("Untrack unsuspendable file descriptors");
588 untrack_std_fds(tracker);
f7c3ffd7
JG
589 check_fd_count(TRACKER_FD_LIMIT + unknown_fds_count +
590 dir_handle_fd_count);
9ca3e8a2 591
f7c3ffd7 592 ret = cleanup_files(tracker, test_directory, files_to_create, handles,
9ca3e8a2
JG
593 output_files);
594 ok(!ret, "Close all opened filesystem handles");
f7c3ffd7
JG
595 ret = rmdir(test_directory);
596 ok(ret == 0, "Test directory is empty");
9ca3e8a2 597 fd_tracker_destroy(tracker);
f7c3ffd7
JG
598 lttng_directory_handle_put(dir_handle);
599 free(test_directory);
600 free(unlinked_files_directory);
9ca3e8a2
JG
601}
602
603/*
604 * Open more files than allowed by the fd tracker's cap and write,
605 * byte-by-byte, and in round-robin, a string. The goal is to force
606 * the fd tracker to suspend and resume the fs_handles often and
607 * verify that the fd cap is always respected.
608 *
609 * The content of the files is also verified at the end.
610 */
611static
612void test_suspendable_restore(void)
613{
614 int ret;
615 const int files_to_create = TRACKER_FD_LIMIT * 10;
616 struct fd_tracker *tracker;
9ca3e8a2
JG
617 char *output_files[files_to_create];
618 struct fs_handle *handles[files_to_create];
619 size_t content_index;
620 int handle_index;
621 bool write_success = true;
622 bool fd_cap_respected = true;
623 bool content_ok = true;
f7c3ffd7
JG
624 struct lttng_directory_handle *dir_handle = NULL;
625 int dir_handle_fd_count;
626 char *test_directory = NULL, *unlinked_files_directory = NULL;
9ca3e8a2
JG
627
628 memset(output_files, 0, sizeof(output_files));
629 memset(handles, 0, sizeof(handles));
630
f7c3ffd7
JG
631 get_temporary_directories(&test_directory, &unlinked_files_directory);
632
633 tracker = fd_tracker_create(unlinked_files_directory, TRACKER_FD_LIMIT);
9ca3e8a2
JG
634 if (!tracker) {
635 return;
636 }
637
f7c3ffd7
JG
638 dir_handle = lttng_directory_handle_create(test_directory);
639 assert(dir_handle);
640 dir_handle_fd_count = !!lttng_directory_handle_uses_fd(dir_handle);
9ca3e8a2 641
f7c3ffd7 642 ret = open_files(tracker, dir_handle, files_to_create, handles,
9ca3e8a2
JG
643 output_files);
644 ok(!ret, "Created %d files with a limit of %d simultaneously-opened file descriptor",
645 files_to_create, TRACKER_FD_LIMIT);
646 diag("Check file descriptor count after opening %u files", files_to_create);
f7c3ffd7
JG
647 check_fd_count(TRACKER_FD_LIMIT + STDIO_FD_COUNT + unknown_fds_count +
648 dir_handle_fd_count);
9ca3e8a2
JG
649
650 for (content_index = 0; content_index < sizeof(file_contents); content_index++) {
651 for (handle_index = 0; handle_index < files_to_create; handle_index++) {
652 int fd;
653 struct fs_handle *handle = handles[handle_index];
654 const char *path = output_files[handle_index];
655
656 fd = fs_handle_get_fd(handle);
657 if (fd < 0) {
658 write_success = false;
659 diag("Failed to restore fs_handle to %s",
660 path);
661 goto skip_write;
662 }
663
664 do {
665 ret = write(fd, file_contents + content_index, 1);
666 } while (ret < 0 && errno == EINTR);
667
668 if (ret != 1) {
669 write_success = false;
670 PERROR("write() to %s failed", path);
671 goto skip_write;
672 }
673
f7c3ffd7
JG
674 if (fd_count() > (TRACKER_FD_LIMIT + STDIO_FD_COUNT +
675 unknown_fds_count +
676 dir_handle_fd_count)) {
9ca3e8a2
JG
677 fd_cap_respected = false;
678 }
679
f7c3ffd7 680 fs_handle_put_fd(handle);
9ca3e8a2
JG
681 }
682 }
683skip_write:
684 ok(write_success, "Wrote reference string to %d files",
685 files_to_create);
686 ok(fd_cap_respected, "FD tracker enforced the file descriptor cap");
687
688 /* Validate the contents of the files. */
689 for (handle_index = 0; handle_index < files_to_create; handle_index++) {
690 struct stat fd_stat;
691 const char *path = output_files[handle_index];
692 char read_buf[sizeof(file_contents)];
693 char *read_pos;
694 size_t to_read = sizeof(read_buf);
695 int fd;
696
f7c3ffd7
JG
697 fd = lttng_directory_handle_open_file(
698 dir_handle, path, O_RDONLY, 0);
9ca3e8a2
JG
699 assert(fd >= 0);
700 ret = fstat(fd, &fd_stat);
701 assert(!ret);
702 if (fd_stat.st_size != sizeof(file_contents)) {
703 diag("Content size of file %s doesn't match, got %" PRId64 ", expected %zu",
704 path, (int64_t) fd_stat.st_size,
705 sizeof(file_contents));
706 content_ok = false;
707 (void) close(fd);
708 break;
709 }
710
711 read_pos = read_buf;
712 do {
713 ret = read(fd, read_pos, to_read);
714 if (ret > 0) {
715 to_read -= ret;
716 read_pos += ret;
717 }
718 } while (to_read && (ret < 0 && errno == EINTR));
719 if (ret < 0) {
720 content_ok = false;
721 PERROR("Failed to read file %s", path);
722 (void) close(fd);
723 break;
724 }
725
726 if (strcmp(file_contents, read_buf)) {
727 content_ok = false;
728 diag("File content doesn't match the expectated string");
729 (void) close(fd);
730 break;
731 }
732 (void) close(fd);
733 }
734 ok(content_ok, "Files contain the expected content");
f7c3ffd7 735 ret = cleanup_files(tracker, test_directory, files_to_create, handles,
9ca3e8a2
JG
736 output_files);
737 ok(!ret, "Close all opened filesystem handles");
f7c3ffd7
JG
738 ret = rmdir(test_directory);
739 ok(ret == 0, "Test directory is empty");
740 fd_tracker_destroy(tracker);
741 lttng_directory_handle_put(dir_handle);
742 free(test_directory);
743 free(unlinked_files_directory);
9ca3e8a2
JG
744}
745
f0faf175
JG
746static
747void test_unlink(void)
748{
749 int ret;
750 struct fd_tracker *tracker;
751 const int handles_to_open = 2;
f0faf175 752 struct fs_handle *handles[handles_to_open];
ae63e134 753 struct fs_handle *new_handle = NULL;
f0faf175 754 struct stat statbuf;
f7c3ffd7
JG
755 struct lttng_directory_handle *dir_handle = NULL;
756 const char file_name[] = "my_file";
757 char *test_directory = NULL, *unlinked_files_directory = NULL;
758 char *unlinked_file_zero = NULL, *unlinked_file_one = NULL;
759 int fd;
760
761 get_temporary_directories(&test_directory, &unlinked_files_directory);
762 ret = asprintf(&unlinked_file_zero, "%s/%u", unlinked_files_directory,
763 0);
764 assert(ret > 0);
765 ret = asprintf(&unlinked_file_one, "%s/%u", unlinked_files_directory,
766 1);
767 assert(ret > 0);
768
769 tracker = fd_tracker_create(unlinked_files_directory, 1);
f0faf175
JG
770 if (!tracker) {
771 return;
772 }
f7c3ffd7
JG
773
774 dir_handle = lttng_directory_handle_create(test_directory);
775 assert(dir_handle);
f0faf175
JG
776
777 /* Open two handles to the same file. */
f7c3ffd7
JG
778 ret = open_same_file(tracker, dir_handle, file_name, handles_to_open,
779 handles);
780 ok(!ret, "Successfully opened %i handles to %s/%s", handles_to_open,
781 test_directory, file_name);
f0faf175
JG
782 if (ret) {
783 return;
784 }
785
786 /*
787 * Unlinking the first handle should cause the file to be renamed
f7c3ffd7 788 * to '0'.
f0faf175
JG
789 */
790 ret = fs_handle_unlink(handles[0]);
f7c3ffd7
JG
791 ok(!ret, "Successfully unlinked the first handle to %s/%s",
792 test_directory, file_name);
f0faf175
JG
793
794 /*
795 * The original file should no longer exist on the file system, and a
f7c3ffd7 796 * new file named '0' should exist.
f0faf175 797 */
f7c3ffd7
JG
798 ok(lttng_directory_handle_stat(dir_handle, file_name, &statbuf) == -1 &&
799 errno == ENOENT,
800 "%s no longer present on file system after unlink",
801 file_name);
802 ok(lttng_directory_handle_stat(
803 dir_handle, unlinked_file_zero, &statbuf) == 0,
804 "%s exists on file system after unlink",
805 unlinked_file_zero);
806
807 /*
808 * It should be possible to use the file descriptors of both handles.
809 * Since only one file descriptor can be opened at once, this should
810 * force the fd_tracker to suspend and restore the handles.
811 */
812 fd = fs_handle_get_fd(handles[0]);
813 ok(fd >= 0, "Got fd from first handle");
814
815 fd = fs_handle_get_fd(handles[1]);
816 ok (fd < 0, "fd tracker does not allow two fds to be used at once");
817
818 fs_handle_put_fd(handles[0]);
819 fd = fs_handle_get_fd(handles[1]);
820 ok(fd >= 0, "Got fd from second handle");
821 fs_handle_put_fd(handles[1]);
f0faf175
JG
822
823 /* The second unlink should fail with -ENOENT. */
824 ret = fs_handle_unlink(handles[1]);
f7c3ffd7
JG
825 ok(ret == -ENOENT,
826 "ENOENT is reported when attempting to unlink the second handle to %s/%s",
827 test_directory, file_name);
f0faf175
JG
828
829 /*
830 * Opening a new handle to 'my_file' should succeed.
831 */
f7c3ffd7
JG
832 ret = open_same_file(tracker, dir_handle, file_name, 1, &new_handle);
833 ok(!ret, "Successfully opened a new handle to previously unlinked file %s/%s",
834 test_directory, file_name);
f0faf175
JG
835 assert(new_handle);
836
837 /*
838 * Unlinking the new handle should cause the file to be renamed
f7c3ffd7 839 * to '1' since '0' already exists.
f0faf175
JG
840 */
841 ret = fs_handle_unlink(new_handle);
f7c3ffd7
JG
842 ok(!ret, "Successfully unlinked the new handle handle to %s/%s",
843 test_directory, file_name);
844 ok(stat(unlinked_file_one, &statbuf) == 0,
845 "%s exists on file system after unlink",
846 unlinked_file_one);
f0faf175
JG
847
848 ret = fs_handle_close(handles[0]);
849 ok(!ret, "Successfully closed the first handle");
850 ret = fs_handle_close(handles[1]);
851 ok(!ret, "Successfully closed the second handle");
852 ret = fs_handle_close(new_handle);
853 ok(!ret, "Successfully closed the third handle");
854
f7c3ffd7
JG
855 ok(lttng_directory_handle_stat(dir_handle, file_name, &statbuf) == -1 &&
856 errno == ENOENT,
857 "%s no longer present on file system after handle close",
858 file_name);
859 ok(lttng_directory_handle_stat(
860 dir_handle, unlinked_file_zero, &statbuf) == -1 &&
861 errno == ENOENT,
862 "%s no longer present on file system after handle close",
863 unlinked_file_zero);
864 ok(lttng_directory_handle_stat(dir_handle, unlinked_file_one,
865 &statbuf) == -1 &&
866 errno == ENOENT,
867 "%s no longer present on file system after handle close",
868 unlinked_file_one);
869
870 ret = rmdir(test_directory);
871 ok(ret == 0, "Test directory is empty");
f0faf175 872 fd_tracker_destroy(tracker);
f7c3ffd7
JG
873 free(test_directory);
874 free(unlinked_files_directory);
875 free(unlinked_file_zero);
876 free(unlinked_file_one);
877 lttng_directory_handle_put(dir_handle);
f0faf175
JG
878}
879
9ca3e8a2
JG
880int main(int argc, char **argv)
881{
882 plan_tests(NUM_TESTS);
883 diag("File descriptor tracker unit tests");
884
885 rcu_register_thread();
886
887 unknown_fds_count = fd_count() - STDIO_FD_COUNT;
888 assert(unknown_fds_count >= 0);
889
890 diag("Unsuspendable - basic");
891 test_unsuspendable_basic();
892 diag("Unsuspendable - callback return values");
893 test_unsuspendable_cb_return();
894 diag("Unsuspendable - duplicate file descriptors");
895 test_unsuspendable_duplicate();
896 diag("Unsuspendable - closing an untracked file descriptor");
897 test_unsuspendable_close_untracked();
898 diag("Unsuspendable - check that file descritptor limit is enforced");
899 test_unsuspendable_limit();
900
901 diag("Suspendable - check that file descritptor limit is enforced");
902 test_suspendable_limit();
903 diag("Suspendable - restoration test");
904 test_suspendable_restore();
905
906 diag("Mixed - check that file descritptor limit is enforced");
907 test_mixed_limit();
908
f0faf175
JG
909 diag("Suspendable - Unlinking test");
910 test_unlink();
911
f7c3ffd7 912 rcu_barrier();
9ca3e8a2
JG
913 rcu_unregister_thread();
914 return exit_status();
915}
This page took 0.088065 seconds and 5 git commands to generate.