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